Skip to content

Commit

Permalink
Prof pic tinder (#106)
Browse files Browse the repository at this point in the history
* admin panel separation

* tinder card

* kinda ready
animations and more detailed data maybe?

* does not build but works xd

* builds

* icons and little mod

* fixes

* set to manufactured button fix

* merge fix

* icon fix

* builds i guess
  • Loading branch information
mozsarmate authored Jan 16, 2025
1 parent b7541d9 commit 6a9c0e4
Show file tree
Hide file tree
Showing 14 changed files with 378 additions and 22 deletions.
30 changes: 30 additions & 0 deletions apps/frontend/src/app/admin/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
'use client';
import { useRouter } from 'next/navigation';
import React from 'react';
import { FiGrid, FiShield, FiUserCheck } from 'react-icons/fi';

import Th1 from '@/components/typography/typography';
import { Button } from '@/components/ui/button';

export default function Page() {
const router = useRouter();
return (
<>
<Th1>Admin</Th1>
<div className='flex w-full gap-4 max-lg:flex-col'>
<Button onClick={() => router.push('/profile-picture-check')}>
<FiUserCheck />
Profileképek ellenőrzése
</Button>
<Button onClick={() => router.push('/roles')}>
<FiShield />
Szerepkörök kezelése
</Button>
<Button onClick={() => router.push('/periods')}>
<FiGrid />
Időszakok kezelése
</Button>
</div>
</>
);
}
13 changes: 9 additions & 4 deletions apps/frontend/src/app/periods/[id]/data-table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ interface DataTableProps<TData, TValue> {
onStatusChange?: (row: TData, status: ApplicationStatus) => void;
onExportPassesClicked: (data: TData[]) => void;
onExportApplicationsClicked: (data: TData[]) => void;
onSetToDistributedClicked: (data: TData[]) => void;
onSetToManufactured: (data: TData[]) => void;
}

export function DataTable<TData, TValue>({
Expand All @@ -59,7 +59,7 @@ export function DataTable<TData, TValue>({
onStatusChange,
onExportApplicationsClicked,
onExportPassesClicked,
onSetToDistributedClicked,
onSetToManufactured,
}: DataTableProps<TData, TValue>) {
const [sorting, setSorting] = React.useState<SortingState>([
{
Expand Down Expand Up @@ -233,12 +233,17 @@ export function DataTable<TData, TValue>({
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button onClick={() => onSetToDistributedClicked(data)} variant='outline'>
<Button onClick={() => onSetToManufactured(data)} variant='outline'>
Nyomtatással kész
</Button>
</TooltipTrigger>
<TooltipContent>
<p>Az összes NYOMTATÁSRA KÉSZ státusszal rendelkező applikációt átállítja KIOSZTOTT-ra</p>
<span className='flex gap-2 items-center'>
Az összes <StatusBadge status={'PREPARED_FOR_PRINT' as ApplicationStatus} />
jelentkezés
<StatusBadge status={'MANUFACTURED' as ApplicationStatus} />
-ra állítása
</span>
</TooltipContent>
</Tooltip>
</TooltipProvider>
Expand Down
8 changes: 4 additions & 4 deletions apps/frontend/src/app/periods/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ export default function Page(props: { params: Promise<{ id: number }> }) {
);

// Set the exported applications to "WAITING_FOR_OPS" status
if (autoChangeStatus === true) {
if (autoChangeStatus) {
for (let i = 0; i < dataToExport.length; i++) {
handleStatusChange(dataToExport[i], ApplicationStatus.WAITING_FOR_OPS);
}
Expand All @@ -179,10 +179,10 @@ export default function Page(props: { params: Promise<{ id: number }> }) {
* This function sets the status of the selected applications which has the
* status {@link ApplicationStatus.PREPARED_FOR_PRINT} to {@link ApplicationStatus.DISTRIBUTED}
*/
const onSetToDistributedClicked = async (data: ApplicationEntity[]) => {
const onSetToManufactured = async (data: ApplicationEntity[]) => {
for (let i = 0; i < data.length; i++) {
if (data[i].status === getStatusKey(ApplicationStatus.PREPARED_FOR_PRINT)) {
handleStatusChange(data[i], ApplicationStatus.DISTRIBUTED);
handleStatusChange(data[i], ApplicationStatus.MANUFACTURED);
}
}
};
Expand Down Expand Up @@ -250,7 +250,7 @@ export default function Page(props: { params: Promise<{ id: number }> }) {
onStatusChange={handleStatusChange}
onExportPassesClicked={onPassExport}
onExportApplicationsClicked={onApplicationsExport}
onSetToDistributedClicked={onSetToDistributedClicked}
onSetToManufactured={onSetToManufactured}
/>
)}
</div>
Expand Down
72 changes: 72 additions & 0 deletions apps/frontend/src/app/profile-picture-check/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
'use client';
import { useRouter } from 'next/navigation';
import React from 'react';

import api from '@/components/network/apiSetup';
import { Th2 } from '@/components/typography/typography';
import { Button } from '@/components/ui/button';
import { Card } from '@/components/ui/card';
import LoadingCard from '@/components/ui/LoadingCard';
import ProfilePictureTinderCard from '@/components/ui/ProfilePictureTinderCard';
import { usePendingPictures } from '@/hooks/usePendingPictures';

export default function Page() {
const [counter, setCounter] = React.useState(0);
const { data, isLoading, mutate } = usePendingPictures();
const [currentPicture, setCurrentPicture] = React.useState(0);
const [isMutating, setIsMutating] = React.useState(false);
const onAccept = async () => {
if (!data) return;
await api.patch(`users/${data![currentPicture].user.authSchId}/profile-picture/ACCEPTED`);
await showNextPicture();
};
const onReject = async () => {
if (!data) return;
await api.patch(`users/${data![currentPicture].user.authSchId}/profile-picture/REJECTED`);
await showNextPicture();
};
const onSkip = async () => {
await showNextPicture();
};
const showNextPicture = async () => {
if (!data) return;
setIsMutating(true);
setCounter(counter + 1);
if (currentPicture === data!.length - 1) {
await mutate();
setCurrentPicture(data.length === 0 ? -1 : 0);
} else {
setCurrentPicture(currentPicture + 1);
}
setIsMutating(false);
};

const router = useRouter();

return (
<Card className='flex flex-col items-center w-full gap-4 p-8'>
{isLoading && <LoadingCard />}
{data && data.length === 0 && <p>Most nincs elbirálandó kép</p>}
{data && currentPicture > -1 && data[currentPicture] && (
<>
<ProfilePictureTinderCard
user={data[currentPicture].user}
onAccept={onAccept}
onReject={onReject}
onSkip={onSkip}
isMutating={isLoading || isMutating}
/>
<Th2>{counter} húzás</Th2>
</>
)}
<Button
onClick={() => {
router.push('/admin');
}}
variant='secondary'
>
Vissza
</Button>
</Card>
);
}
14 changes: 4 additions & 10 deletions apps/frontend/src/components/ui/LoginButton.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use client';
import { useRouter } from 'next/navigation';
import { FiGrid, FiLogIn, FiShield, FiUser } from 'react-icons/fi';
import { FiKey, FiLogIn, FiUser } from 'react-icons/fi';

import { Button } from '@/components/ui/button';
import useProfile from '@/hooks/useProfile';
Expand All @@ -22,15 +22,9 @@ export default function LoginButton({ version }: { version: number }) {
{user && (
<div className='flex gap-2 items-center'>
{(user.role === 'BODY_MEMBER' || user.role === 'BODY_ADMIN' || user.role === 'SUPERUSER') && (
<Button variant='secondary' onClick={() => router.push('/roles')}>
<FiShield />
{version === 1 ? 'Jogosultságok' : ''}
</Button>
)}
{(user.role === 'BODY_MEMBER' || user.role === 'BODY_ADMIN' || user.role === 'SUPERUSER') && (
<Button variant='secondary' onClick={() => router.push('/periods')}>
<FiGrid />
{version === 1 ? 'Időszakok' : ''}
<Button variant='secondary' onClick={() => router.push('/admin')}>
<FiKey />
Admin
</Button>
)}

Expand Down
6 changes: 5 additions & 1 deletion apps/frontend/src/components/ui/MemberProfileData.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { UseFormReturn } from 'react-hook-form';
import { LuUserCheck } from 'react-icons/lu';

import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from '@/components/ui/form';
Expand Down Expand Up @@ -26,7 +27,10 @@ export default function MemberProfileData({
return (
<Card>
<CardHeader>
<CardTitle>Körtag beállítások</CardTitle>
<CardTitle>
<LuUserCheck />
Körtag beállítások
</CardTitle>
</CardHeader>
<CardContent className='md:grid-cols-2 grid gap-4'>
<FormField
Expand Down
11 changes: 9 additions & 2 deletions apps/frontend/src/components/ui/ProfileForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import { zodResolver } from '@hookform/resolvers/zod';
import React, { useEffect } from 'react';
import { useForm } from 'react-hook-form';
import { LuBuilding2, LuCrown } from 'react-icons/lu';
import { z } from 'zod';

import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
Expand Down Expand Up @@ -94,7 +95,10 @@ export default function ProfileForm() {
<Card>
<CardHeader className='flex items-start flex-row justify-between'>
<div>
<CardTitle>Személyes adatok</CardTitle>
<CardTitle>
<LuCrown />
Személyes adatok
</CardTitle>
</div>
</CardHeader>
<CardContent className='w-full md:grid-cols-2 md:grid gap-4 '>
Expand Down Expand Up @@ -128,7 +132,10 @@ export default function ProfileForm() {
</Card>
<Card>
<CardHeader>
<CardTitle>Kollégiumi bentlakás</CardTitle>
<CardTitle>
<LuBuilding2 />
Kollégiumi bentlakás
</CardTitle>
</CardHeader>
<CardContent className='md:grid-cols-2 grid gap-4'>
<FormField
Expand Down
95 changes: 95 additions & 0 deletions apps/frontend/src/components/ui/ProfilePictureTinderCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import React, { useState } from 'react';
import { FiCheck, FiX } from 'react-icons/fi';

import { Button } from '@/components/ui/button';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { useKeyboardShortcut } from '@/lib/useKeyboardShortcut';
import { UserEntity } from '@/types/user-entity';

type ProfilePictureTinderCardProps = {
user: UserEntity;
onAccept: () => Promise<void>;
onReject: () => Promise<void>;
onSkip: () => Promise<void>;
isMutating: boolean;
};

export default function ProfilePictureTinderCard({
user,
onAccept,
onReject,
onSkip,
isMutating,
}: ProfilePictureTinderCardProps) {
const [slideDirection, setSlideDirection] = useState<string | null>(null);
const [isSliding, setIsSliding] = useState(false);

const handleSlideOut = async (direction: 'left' | 'right') => {
setIsSliding(true);
setSlideDirection(direction);

if (direction === 'left') {
await onReject().then(() => {
setTimeout(() => {
setIsSliding(false);
setSlideDirection(null);
}, 200);
});
} else {
await onAccept().then(() => {
setTimeout(() => {
setIsSliding(false);
setSlideDirection(null);
}, 200);
});
}
};

useKeyboardShortcut(['f'], () => handleSlideOut('left'));
useKeyboardShortcut(['j'], () => handleSlideOut('right'));

return (
<Card className='w-fit flex flex-col items-center overflow-hidden'>
<CardHeader>
<CardTitle>Elfogadod ezt a képet?</CardTitle>
</CardHeader>
<CardContent className='flex flex-col items-center gap-4'>
<div className='relative'>
<div
className={`overflow-hidden rounded transition-all duration-500 ${
slideDirection === 'left' ? '-translate-x-96 opacity-0' : ''
} ${slideDirection === 'right' ? 'translate-x-96 opacity-0' : ''} ${
isSliding ? '' : 'translate-x-0 opacity-100 duration-150 transition-opacity'
}`}
>
<img
src={
isMutating
? ''
: `${process.env.NEXT_PUBLIC_API_URL}/users/${user.authSchId}/profile-picture?cb=${Date.now()}`
}
className='h-96 rounded hover:animate-zoom-in object-cover w-72'
/>
<div className='absolute bottom-0 left-0 text-white bg-gradient-to-t from-black to-transparent h-fit w-full p-2 rounded-b'>
<p className='font-bold'>{isMutating ? 'Betöltés' : user.fullName}</p>
<p className='text-xs'>{isMutating ? '...' : user.email}</p>
</div>
</div>
</div>
<div className='flex gap-4'>
<Button variant='destructive' onClick={() => handleSlideOut('left')}>
<div className='border p-0.5 text-xs rounded px-1.5'>F</div>
<FiX />
</Button>
<Button variant='secondary' onClick={onSkip}>
Átugrom
</Button>
<Button onClick={() => handleSlideOut('right')}>
<FiCheck />
<div className='border p-0.5 text-xs rounded px-1.5'>J</div>
</Button>
</div>
</CardContent>
</Card>
);
}
Loading

0 comments on commit 6a9c0e4

Please sign in to comment.