Skip to content

Building Users Collection + Sorting Events #259

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
185 changes: 109 additions & 76 deletions src/components/Accounts/AccountsPage.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import { useContext, useState, useEffect } from 'react';
import { IconPlus, IconSearch, IconTrash } from '@tabler/icons-react';
import {
IconPlus,
IconSearch,
IconTrash,
IconChevronDown,
} from '@tabler/icons-react';

import {
checkIfLoggedIn,
getAccountId,
getAllEventsForUser,
deleteEvent,
getParsedAccountPageEventsForUser,
} from '../../firebase/events';
import { logout } from '../../firebase/auth';

Expand All @@ -17,55 +22,21 @@ import { GAPIContext } from '../../firebase/gapiContext';
import { LoadingAnim } from '../utils/components/LoadingAnim';
import LoginButton from '../utils/components/LoginButton';
import CopyCodeButton from '../utils/components/CopyCodeButton';
import Button from '../utils/components/Button';
import ButtonSmall from '../utils/components/ButtonSmall';

interface AccountsPageEvent {
export interface AccountsPageEvent {
name: string;
id: string;
dates: string;
startTime: string;
endTime: string;
location: string;
iAmCreator: boolean;
dateCreated: Date;
lastModified: Date;
}

/**
* Parses the backend event object into a type that the AccountsPage component understands.
* @param events Event[]
* @returns AccountsPageEvent[]
*/
const parseEventObjectForAccountsPage = (
events: Event[]
): AccountsPageEvent[] => {
const accountPageEvents: AccountsPageEvent[] = [];
events.forEach((event) => {
accountPageEvents.push({
name: event.details.name,
id: event.publicId,
dates: event.details.chosenStartDate
? event.details.chosenStartDate?.toLocaleDateString()
: 'TBD',
startTime: event.details.chosenStartDate
? event.details.chosenStartDate?.toLocaleTimeString('en-US', {
hour: '2-digit',
minute: '2-digit',
hour12: true,
})
: 'TBD',
endTime: event.details.chosenEndDate
? event.details.chosenEndDate?.toLocaleTimeString('en-US', {
hour: '2-digit',
minute: '2-digit',
hour12: true,
})
: 'TBD',
location: event.details.chosenLocation || 'TBD',
iAmCreator: event.details.adminAccountId === getAccountId(),
});
});

return accountPageEvents;
};

/**
* Page Component. Renders the events associated with a logged in Google account.
* Renders nothing if no events are associated or the user is logged in anonymously
Expand All @@ -80,9 +51,11 @@ export default function AccountsPage() {
const accountID = getAccountId();

if (accountID && accountID !== '') {
await getAllEventsForUser(getAccountId()).then((eventsUnparsed) => {
setEvents(parseEventObjectForAccountsPage(eventsUnparsed) || []);
});
await getParsedAccountPageEventsForUser(accountID).then(
(parsedEvents) => {
setEvents(parsedEvents);
}
);
} else {
setEvents([]);
}
Expand All @@ -95,51 +68,111 @@ export default function AccountsPage() {

const nav = useNavigate();
const [filter, setFilter] = useState('');

const [events, setEvents] = useState<AccountsPageEvent[] | undefined>();
const [hasDeletedEvent, setHasDeletedEvent] = useState<boolean>(false);
const [sortBy, setSortBy] = useState<'dateCreated' | 'lastModified'>(
'lastModified'
);

const handleInputChange = (e: any) => {
setFilter(e.target.value.toLowerCase());
};

const getSortedEvents = (events: AccountsPageEvent[]) => {
// return events; // remove later

console.log(events);

return [...events].sort((a, b) => {
let dateA = sortBy === 'dateCreated' ? a.dateCreated : a.lastModified;
let dateB = sortBy === 'dateCreated' ? b.dateCreated : b.lastModified;

dateA = new Date(dateA);
dateB = new Date(dateB);

console.log(dateA, dateB);
if (dateA === undefined || dateB === undefined) {
console.log('Date is undefined');
return 0;
}
return dateB.getTime() - dateA.getTime();
});
};

return (
<div className="min-h-screen flex flex-col items-center">
<div className="w-full max-w-full pt-2 sm:pt-4 pb-10 sm:pb-14 px-5 xs:px-8 md:px-12 lg:px-16 xl:px-20 max-w-8xl flex flex-col gap-6 xs:gap-8 sm:gap-10 flex-grow w-full">
<div className="flex flex-col sm:flex-row justify-between gap-4 sm:gap-6 md:gap-8">
<h2 className="text-3xl sm:text-xl md:text-2xl lg:text-3xl xl:text-4xl font-bold text-text dark:text-text-dark">
<div className="w-full max-w-[1400px] pt-4 pb-10 px-5 xs:px-8 md:px-12 lg:px-16 xl:px-20 flex flex-col gap-4">
{/* Header with Title */}
<div className="flex items-center justify-between">
<h2 className="text-3xl sm:text-xl md:text-2xl lg:text-3xl xl:text-4xl font-bold text-text dark:text-white">
Your Events
</h2>

<div className="flex flex-col gap-3 sm:flex-row sm:gap-4 w-full sm:w-auto">
<div className="relative flex-1 min-w-[250px]">
<div className="absolute inset-y-0 left-3 flex items-center pointer-events-none">
<IconSearch className="w-5 h-5 text-gray-400 dark:text-gray-500" />
</div>
<input
type="text"
placeholder="Search events..."
onChange={handleInputChange}
className="w-full pl-10 pr-4 py-2.5 md:py-4 text-sm md:text-base bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-lg transition-all
focus:border-primary focus:ring-2 focus:ring-primary/20
hover:border-gray-400 dark:hover:border-gray-500
placeholder-gray-400 dark:placeholder-gray-500
dark:text-white min-h-[40px] md:min-h-[60px]"
/>
</div>
{/* Desktop Button */}
<div className="hidden sm:block">
<Button
onClick={() => nav('/dayselect')}
bgColor="primary"
textColor="white"
className="inline-flex items-center gap-2"
>
<IconPlus />
Create Event
</Button>
</div>

<button
className="w-full sm:w-auto flex items-center justify-center gap-2 px-5 py-2 md:py-4 bg-gradient-to-r from-primary to-primary-dark dark:from-blue-900 dark:to-blue-600
text-white font-semibold rounded-lg transition-all transition-transform duration-300
transform hover:scale-102 active:translate-y-0
shadow-md hover:shadow-lg
focus:outline-none focus:ring-2 focus:ring-primary/30
whitespace-nowrap min-h-[40px] md:min-h-[60px] text-sm md:text-base"
{/* Mobile Button */}
<div className="sm:hidden">
<ButtonSmall
onClick={() => nav('/dayselect')}
bgColor="primary"
textColor="white"
className="inline-flex items-center gap-2"
>
<IconPlus className="w-5 h-5 md:w-6 md:h-6" />
<span>Create Event</span>
</button>
<IconPlus />
Create
</ButtonSmall>
</div>
</div>

{/* Controls Row */}
<div className="flex flex-col sm:flex-row gap-3">
{/* Search Input */}
<div className="relative flex-1">
<div className="absolute inset-y-0 left-3 flex items-center pointer-events-none">
<IconSearch className="w-4 h-4 text-gray-400" />
</div>
<input
type="text"
placeholder="Search events..."
onChange={handleInputChange}
className="w-full pl-9 pr-3 py-2 bg-white dark:bg-gray-800
border border-gray-200 dark:border-gray-700 rounded-lg text-sm
transition-all hover:border-gray-300 dark:hover:border-gray-600
focus:border-primary/50 focus:ring-2 focus:ring-primary/20
text-gray-900 dark:text-white"
/>
</div>

{/* Sort Dropdown */}
<div className="relative min-w-[160px]">
<select
value={sortBy}
onChange={(e) =>
setSortBy(e.target.value as 'dateCreated' | 'lastModified')
}
className="w-full appearance-none pl-3 pr-8 py-2 bg-white dark:bg-gray-800
border border-gray-200 dark:border-gray-700 rounded-lg text-sm
text-gray-600 dark:text-gray-300 cursor-pointer transition-all
hover:border-gray-300 dark:hover:border-gray-600
focus:border-primary/50 focus:ring-2 focus:ring-primary/20"
>
<option value="lastModified">Last Modified</option>
<option value="dateCreated">Date Created</option>
</select>
<div className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2">
<IconChevronDown className="w-4 h-4 text-gray-400" />
</div>
</div>
</div>

Expand All @@ -149,8 +182,8 @@ export default function AccountsPage() {
</div>
) : undefined}
{events && events.length != 0 ? (
<div className="space-y-4 md:space-y-0 md:grid md:grid-cols-2 xl:grid-cols-3 gap-4 xs:gap-5 sm:gap-6 md:gap-7 lg:gap-8 xl:gap-9">
{events
<div className="space-y-4 md:space-y-0 md:grid md:grid-cols-2 xl:grid-cols-3 pt-4 gap-4 xs:gap-5 sm:gap-6 md:gap-7 lg:gap-8 xl:gap-9">
{getSortedEvents(events)
.filter(
(e) =>
e.id.toLowerCase().includes(filter) ||
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,8 @@ export const DaySelectComponent = () => {
startDate,
endDate,
zoomLink,
timezone
timezone,
new Date() // dateCreated
)
.then((ev) => {
navigate('/timeselect/' + ev?.publicId);
Expand All @@ -217,7 +218,8 @@ export const DaySelectComponent = () => {
startDate,
endDate,
zoomLink,
timezone
timezone,
new Date() // dateCreated
)
.then((ev) => {
navigate('/timeselect/' + ev?.publicId);
Expand Down
3 changes: 3 additions & 0 deletions src/components/utils/components/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ interface Props {
py?: string; // Padding-top and padding-bottom
themeGradient?: boolean;
bolded?: boolean; // Font weight
className?: string; // Add this line
}

export default function Button({
Expand All @@ -22,6 +23,7 @@ export default function Button({
textSize = 'lg',
themeGradient = true,
bolded = true,
className = '',
}: Props) {
const borderRadius = rounded === 'full' ? 'rounded-full' : 'rounded-lg';
const textSizeClass =
Expand All @@ -38,6 +40,7 @@ export default function Button({
${textSizeClass} text-${textColor} ${borderRadius}
${bgColor === `primary` ? `dark:bg-blue-700` : ``}
${bolded ? `font-bold` : `font-semibold`}
${className}
`}
onClick={onClick}
disabled={disabled}
Expand Down
3 changes: 3 additions & 0 deletions src/components/utils/components/ButtonSmall.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ interface Props {
children: React.ReactNode;
disabled?: boolean;
themeGradient?: boolean;
className?: string;
}

export default function ButtonSmall({
Expand All @@ -14,6 +15,7 @@ export default function ButtonSmall({
children,
disabled = false,
themeGradient = true,
className = ``,
}: Props) {
return (
<button
Expand All @@ -22,6 +24,7 @@ export default function ButtonSmall({
? `bg-gradient-to-r from-primary to-primary-dark dark:from-blue-900 dark:to-blue-600`
: `bg-${bgColor}`
} ${bgColor === `primary` ? `dark:bg-blue-700` : ``}
${className}
`}
onClick={onClick}
disabled={disabled}
Expand Down
Loading