-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: availability card component and display (#38)
* created querying functions * created AvailabilityCard component and added to general page * changed styling and updates colors file to include new pomegranate color * resolved ESLint errors * added message for no availabilities * updated to reflect new design and implemented content moving when menubar is expanded * changed design for date range on availability card
- Loading branch information
Showing
15 changed files
with
416 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
'use client'; | ||
|
||
import React, { useEffect, useState } from 'react'; | ||
import { | ||
fetchAllAvailabilities, | ||
fetchDatesByAvailabilityID, | ||
} from '@/api/supabase/queries/availability'; | ||
import AvailabilityCard from '@/components/AvailabilityCard/AvailabilityCard'; | ||
import MenuBar from '@/components/MenuBar/MenuBar'; | ||
import Add from '@/public/images/add.svg'; | ||
import COLORS from '@/styles/colors'; | ||
import { H3 } from '@/styles/text'; | ||
import { Availabilities, AvailableDates } from '@/types/schema'; | ||
import * as styles from './styles'; | ||
|
||
type AvailabilitiesByYear = { | ||
[year: string]: { | ||
availability: Availabilities; | ||
available_dates: AvailableDates[]; | ||
}[]; | ||
}; | ||
|
||
export default function AvailabilityPage() { | ||
const [groupedByYear, setGroupedByYear] = useState<AvailabilitiesByYear>({}); | ||
const [isLoading, setIsLoading] = useState(true); | ||
const [menuExpanded, setMenuExpanded] = useState(false); // Track the expanded state of the menu | ||
|
||
useEffect(() => { | ||
async function fetchAndGroupData() { | ||
try { | ||
// Step 1: Fetch all availabilities | ||
const availabilities = await fetchAllAvailabilities(); | ||
|
||
if (!availabilities) { | ||
return; | ||
} | ||
|
||
// Step 2: Group by year while fetching associated dates | ||
const grouped: AvailabilitiesByYear = {}; | ||
for (const availability of availabilities) { | ||
const availableDates = await fetchDatesByAvailabilityID( | ||
availability.availability_id, | ||
); | ||
|
||
const year = availableDates?.[0]?.available_date | ||
? new Date(availableDates[0].available_date) | ||
.getFullYear() | ||
.toString() | ||
: null; | ||
|
||
//don't display availability cards that have no availabilities associated | ||
if (year == null) { | ||
continue; | ||
} | ||
|
||
if (!grouped[year]) { | ||
grouped[year] = []; | ||
} | ||
|
||
grouped[year].push({ | ||
availability, | ||
available_dates: availableDates ?? [], | ||
}); | ||
} | ||
for (const year in grouped) { | ||
grouped[year].sort((a, b) => { | ||
const firstDateA = new Date( | ||
a.available_dates[0]?.available_date ?? 0, | ||
).getTime(); | ||
const firstDateB = new Date( | ||
b.available_dates[0]?.available_date ?? 0, | ||
).getTime(); | ||
return firstDateA - firstDateB; | ||
}); | ||
} | ||
|
||
setGroupedByYear(grouped); | ||
setIsLoading(false); // Stop loading after data fetch | ||
} catch (error) { | ||
console.error('Error fetching data:', error); | ||
} | ||
} | ||
|
||
fetchAndGroupData(); | ||
}, []); | ||
|
||
return ( | ||
<div> | ||
<MenuBar setMenuExpanded={setMenuExpanded} />{' '} | ||
{/* Pass function to update state */} | ||
<styles.Page $menuExpanded={menuExpanded}> | ||
<styles.AllAvailabilitiesHolder> | ||
<styles.TitleContainer> | ||
<H3 $fontWeight="500" $color="#000" $align="left"> | ||
Availabilities | ||
</H3> | ||
<styles.AddImage src={Add} alt="add icon" /> | ||
</styles.TitleContainer> | ||
{/* Check if there are no availabilities */} | ||
{isLoading ? ( | ||
<styles.message | ||
$fontWeight="400" | ||
$color={COLORS.gray11} | ||
$align="center" | ||
> | ||
Loading availabilities... | ||
</styles.message> | ||
) : Object.keys(groupedByYear).length === 0 ? ( | ||
<styles.message | ||
$fontWeight="400" | ||
$color={COLORS.gray11} | ||
$align="center" | ||
> | ||
No availabilities yet, | ||
<br /> | ||
add one with the plus sign! | ||
</styles.message> | ||
) : ( | ||
Object.entries(groupedByYear).map(([year, availabilities]) => ( | ||
<div key={year}> | ||
<styles.YearText $fontWeight="500" $color="#000" $align="left"> | ||
{year} | ||
</styles.YearText> | ||
{availabilities.map(({ availability, available_dates }) => ( | ||
<AvailabilityCard | ||
key={availability.availability_id} | ||
availability={availability} | ||
availableDates={available_dates} | ||
/> | ||
))} | ||
</div> | ||
)) | ||
)} | ||
</styles.AllAvailabilitiesHolder> | ||
</styles.Page> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
'use client'; | ||
|
||
import NextImage from 'next/image'; | ||
import styled from 'styled-components'; | ||
import { H6, P } from '@/styles/text'; | ||
|
||
export const Page = styled.main<{ $menuExpanded: boolean }>` | ||
display: flex; | ||
min-width: 100%; | ||
min-height: 100vh; | ||
justify-content: center; | ||
overflow: hidden; | ||
margin-left: ${({ $menuExpanded }) => | ||
$menuExpanded ? '10%' : '0'}; /* Fixed margin for the expanded menu */ | ||
transition: margin-left 0.3s ease; /* Smooth transition */ | ||
`; | ||
|
||
export const AllAvailabilitiesHolder = styled.main` | ||
display: flex; | ||
width: 28.75%; | ||
flex-direction: column; | ||
`; | ||
|
||
export const TitleContainer = styled.main` | ||
display: flex; | ||
margin-top: 4.43rem; | ||
margin-bottom: 2.62rem; | ||
justify-content: space-between; | ||
align-items: center; | ||
width: 100%; | ||
`; | ||
|
||
export const YearText = styled(H6)` | ||
margin-bottom: 1.5rem; | ||
`; | ||
|
||
export const AddImage = styled(NextImage)` | ||
width: 1.5rem; | ||
height: 1.5rem; | ||
`; | ||
|
||
export const message = styled(P)` | ||
text-align: center; | ||
margin-top: 5.75rem; | ||
`; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
import React from 'react'; | ||
import Arrow from '@/public/images/white_back.svg'; | ||
import COLORS from '@/styles/colors'; | ||
import { H5, P } from '@/styles/text'; | ||
import { Availabilities, AvailableDates } from '@/types/schema'; | ||
import * as styles from './styles'; | ||
|
||
interface AvailabilityCardProps { | ||
availability: Availabilities; | ||
availableDates: AvailableDates[]; | ||
} | ||
|
||
export default function AvailabilityCard({ | ||
availability, | ||
availableDates, | ||
}: AvailabilityCardProps) { | ||
const dateRange = | ||
availableDates.length > 0 | ||
? { | ||
earliest: new Date( | ||
Math.min( | ||
...availableDates.map(date => | ||
new Date(date.available_date).getTime(), | ||
), | ||
), | ||
), | ||
latest: new Date( | ||
Math.max( | ||
...availableDates.map(date => | ||
new Date(date.available_date).getTime(), | ||
), | ||
), | ||
), | ||
} | ||
: null; | ||
|
||
// Format the dates | ||
let formattedRange = dateRange | ||
? `${dateRange.earliest.toLocaleString('default', { | ||
month: 'short', | ||
day: 'numeric', | ||
})} - ${dateRange.latest.toLocaleString('default', { | ||
month: 'short', | ||
day: 'numeric', | ||
})}` | ||
: 'No dates available'; | ||
|
||
if (dateRange && dateRange.earliest.getDate() == dateRange.latest.getDate()) { | ||
formattedRange = `${dateRange.earliest.toLocaleString('default', { | ||
month: 'short', | ||
day: 'numeric', | ||
})}`; | ||
} | ||
|
||
return ( | ||
<styles.AvailabilityContainer> | ||
<styles.AvailabilityHeader> | ||
<styles.AvailabilityTitle> | ||
<H5 $fontWeight="500" $color={COLORS.bread1} $align="left"> | ||
{availability.name} | ||
</H5> | ||
<P $fontWeight="400" $color={COLORS.gray4} $align="left"> | ||
{formattedRange} | ||
</P> | ||
</styles.AvailabilityTitle> | ||
<styles.Arrow src={Arrow} alt="arrow" /> | ||
</styles.AvailabilityHeader> | ||
<styles.Content> | ||
<div> | ||
<styles.SubHeader> | ||
<P $fontWeight="500" $color={COLORS.gray12} $align="left"> | ||
Description | ||
</P> | ||
<styles.TruncatedText | ||
$fontWeight="400" | ||
$color={COLORS.gray11} | ||
$align="left" | ||
> | ||
{availability.additional_info} | ||
</styles.TruncatedText> | ||
</styles.SubHeader> | ||
</div> | ||
</styles.Content> | ||
</styles.AvailabilityContainer> | ||
); | ||
} |
Oops, something went wrong.