feat(events): add deletion button and style toaster for light mode
This commit is contained in:
parent
8bbb7e4c85
commit
42e1b69720
7 changed files with 128 additions and 32 deletions
|
@ -1,25 +1,42 @@
|
|||
'use client';
|
||||
|
||||
import React from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import Logo from '@/components/misc/logo';
|
||||
import { ThemePicker } from '@/components/misc/theme-picker';
|
||||
import { Card, CardContent, CardHeader } from '@/components/ui/card';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { useGetApiEventEventID } from '@/generated/api/event/event';
|
||||
import {
|
||||
useDeleteApiEventEventID,
|
||||
useGetApiEventEventID,
|
||||
} from '@/generated/api/event/event';
|
||||
import { useGetApiUserMe } from '@/generated/api/user/user';
|
||||
import { RedirectButton } from '@/components/buttons/redirect-button';
|
||||
import { useSession } from 'next-auth/react';
|
||||
import ParticipantListEntry from '@/components/custom-ui/participant-list-entry';
|
||||
import { useParams } from 'next/navigation';
|
||||
import { useParams, useRouter } from 'next/navigation';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { ToastInner } from '@/components/misc/toast-inner';
|
||||
import { toast } from 'sonner';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from '@/components/ui/dialog';
|
||||
|
||||
export default function ShowEvent() {
|
||||
const session = useSession();
|
||||
const router = useRouter();
|
||||
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
||||
|
||||
const { eventId } = useParams<{ eventId: string }>();
|
||||
const { eventID: eventID } = useParams<{ eventID: string }>();
|
||||
|
||||
// Fetch event data
|
||||
const { data: eventData, isLoading, error } = useGetApiEventEventID(eventId);
|
||||
const { data: eventData, isLoading, error } = useGetApiEventEventID(eventID);
|
||||
const { data: userData, isLoading: userLoading } = useGetApiUserMe();
|
||||
const deleteEvent = useDeleteApiEventEventID();
|
||||
|
||||
if (isLoading || userLoading) {
|
||||
return (
|
||||
|
@ -54,9 +71,6 @@ export default function ShowEvent() {
|
|||
|
||||
return (
|
||||
<div className='flex flex-col items-center justify-center h-screen'>
|
||||
<div className='absolute top-4 right-4'>
|
||||
<ThemePicker />
|
||||
</div>
|
||||
<Card className='w-[80%] max-w-screen p-0 gap-0 max-xl:w-[95%] max-h-[90vh] overflow-auto'>
|
||||
<CardHeader className='p-0 m-0 gap-0' />
|
||||
|
||||
|
@ -150,10 +164,65 @@ export default function ShowEvent() {
|
|||
</div>
|
||||
|
||||
<div className='flex flex-row gap-2 justify-end mt-4 mb-6'>
|
||||
<div className='w-[20%] grid max-sm:w-full'>
|
||||
{session.data?.user?.id === event.organizer.id ? (
|
||||
<Dialog
|
||||
open={deleteDialogOpen}
|
||||
onOpenChange={setDeleteDialogOpen}
|
||||
>
|
||||
<DialogTrigger asChild>
|
||||
<Button variant='destructive' className='w-full'>
|
||||
delete
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>Delete Event</DialogTitle>
|
||||
<DialogDescription>
|
||||
Are you sure you want to delete the event “
|
||||
{event.title}”? This action cannot be undone.
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<DialogFooter>
|
||||
<Button
|
||||
variant='secondary'
|
||||
onClick={() => setDeleteDialogOpen(false)}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
variant='muted'
|
||||
onClick={() => {
|
||||
deleteEvent.mutate(
|
||||
{ eventID: event.id },
|
||||
{
|
||||
onSuccess: () => {
|
||||
router.push('/home');
|
||||
toast.custom((t) => (
|
||||
<ToastInner
|
||||
toastId={t}
|
||||
title='Event deleted'
|
||||
description={event?.title}
|
||||
variant='success'
|
||||
/>
|
||||
));
|
||||
},
|
||||
},
|
||||
);
|
||||
setDeleteDialogOpen(false);
|
||||
}}
|
||||
>
|
||||
Delete
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
) : null}
|
||||
</div>
|
||||
<div className='w-[20%] grid max-sm:w-full'>
|
||||
{session.data?.user?.id === event.organizer.id ? (
|
||||
<RedirectButton
|
||||
redirectUrl={`/events/edit/${eventId}`}
|
||||
redirectUrl={`/events/edit/${eventID}`}
|
||||
buttonText='edit'
|
||||
className='w-full'
|
||||
/>
|
|
@ -1,4 +1,3 @@
|
|||
import { ThemePicker } from '@/components/misc/theme-picker';
|
||||
import { Card, CardContent, CardHeader } from '@/components/ui/card';
|
||||
import EventForm from '@/components/forms/event-form';
|
||||
import { Suspense } from 'react';
|
||||
|
@ -11,7 +10,6 @@ export default async function Page({
|
|||
const eventID = (await params).eventID;
|
||||
return (
|
||||
<div className='flex flex-col items-center justify-center h-screen'>
|
||||
<div className='absolute top-4 right-4'>{<ThemePicker />}</div>
|
||||
<Card className='w-[80%] max-w-screen p-0 gap-0 max-xl:w-[95%] max-h-[90vh] overflow-auto'>
|
||||
<CardHeader className='p-0 m-0 gap-0' />
|
||||
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
import { RedirectButton } from '@/components/buttons/redirect-button';
|
||||
import EventListEntry from '@/components/custom-ui/event-list-entry';
|
||||
import { ThemePicker } from '@/components/misc/theme-picker';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { useGetApiEvent } from '@/generated/api/event/event';
|
||||
|
||||
|
@ -19,10 +18,6 @@ export default function Events() {
|
|||
|
||||
return (
|
||||
<div className='relative h-screen flex flex-col items-center'>
|
||||
<div className='absolute top-4 right-4'>
|
||||
<ThemePicker />
|
||||
</div>
|
||||
|
||||
{/* Heading */}
|
||||
<h1 className='text-3xl font-bold mt-8 mb-4 text-center z-10'>
|
||||
My Events
|
|
@ -50,11 +50,23 @@
|
|||
--active-secondary: oklch(0.4254 0.133 272.15);
|
||||
--disabled-secondary: oklch(0.4937 0.1697 271.26 / 0.5);
|
||||
|
||||
--destructive: oklch(60.699% 0.20755 25.945);
|
||||
--hover-destructive: oklch(60.699% 0.20755 25.945 / 0.8);
|
||||
--active-destructive: oklch(50.329% 0.17084 25.842);
|
||||
--disabled-destructive: oklch(60.699% 0.20755 25.945 / 0.4);
|
||||
|
||||
--muted: var(--color-neutral-700);
|
||||
--hover-muted: var(--color-neutral-600);
|
||||
--active-muted: var(--color-neutral-400);
|
||||
--disabled-muted: var(--color-neutral-400);
|
||||
|
||||
--toaster-default-bg: var(--color-neutral-150);
|
||||
--toaster-success-bg: oklch(54.147% 0.09184 144.208);
|
||||
--toaster-error-bg: oklch(52.841% 0.10236 27.274);
|
||||
--toaster-info-bg: oklch(44.298% 0.05515 259.369);
|
||||
--toaster-warning-bg: oklch(61.891% 0.07539 102.943);
|
||||
--toaster-notification-bg: var(--color-neutral-150);
|
||||
|
||||
--card: var(--neutral-800);
|
||||
|
||||
--sidebar-width-icon: 32px;
|
||||
|
@ -81,8 +93,6 @@
|
|||
|
||||
--accent-foreground: oklch(0.21 0.034 264.665);
|
||||
|
||||
--destructive: oklch(0.577 0.245 27.325);
|
||||
|
||||
--border: oklch(0.928 0.006 264.531);
|
||||
|
||||
--input: oklch(0.928 0.006 264.531);
|
||||
|
@ -232,11 +242,23 @@ p {
|
|||
--color-active-secondary: var(--active-secondary);
|
||||
--color-disabled-secondary: var(--disabled-secondary);
|
||||
|
||||
--color-destructive: var(--destructive);
|
||||
--color-hover-destructive: var(--hover-destructive);
|
||||
--color-active-destructive: var(--active-destructive);
|
||||
--color-disabled-destructive: var(--disabled-destructive);
|
||||
|
||||
--color-muted: var(--muted);
|
||||
--color-hover-muted: var(--hover-muted);
|
||||
--color-active-muted: var(--active-muted);
|
||||
--color-disabled-muted: var(--disabled-muted);
|
||||
|
||||
--color-toaster-default-bg: var(--toaster-default-bg);
|
||||
--color-toaster-success-bg: var(--toaster-success-bg);
|
||||
--color-toaster-error-bg: var(--toaster-error-bg);
|
||||
--color-toaster-info-bg: var(--toaster-info-bg);
|
||||
--color-toaster-warning-bg: var(--toaster-warning-bg);
|
||||
--color-toaster-notification-bg: var(--toaster-notification-bg);
|
||||
|
||||
/* Custom values */
|
||||
|
||||
--radius-sm: calc(var(--radius) - 4px);
|
||||
|
@ -277,8 +299,6 @@ p {
|
|||
|
||||
--color-accent-foreground: var(--accent-foreground);
|
||||
|
||||
--color-destructive: var(--destructive);
|
||||
|
||||
--color-border: var(--border);
|
||||
|
||||
--color-input: var(--input);
|
||||
|
@ -354,11 +374,23 @@ p {
|
|||
--active-secondary: oklch(0.4471 0.15 271.61);
|
||||
--disabled-secondary: oklch(0.6065 0.213 271.11 / 0.4);
|
||||
|
||||
--destructive: oklch(0.58 0.2149 27.13);
|
||||
--hover-destructive: oklch(0.58 0.2149 27.13 / 0.8);
|
||||
--active-destructive: oklch(45.872% 0.16648 26.855);
|
||||
--disabled-destructive: oklch(0.58 0.2149 27.13 / 0.4);
|
||||
|
||||
--muted: var(--color-neutral-650);
|
||||
--hover-muted: var(--color-neutral-500);
|
||||
--active-muted: var(--color-neutral-400);
|
||||
--disabled-muted: var(--color-neutral-400);
|
||||
|
||||
--toaster-default-bg: var(--color-neutral-150);
|
||||
--toaster-success-bg: var(--color-green-200);
|
||||
--toaster-error-bg: var(--color-red-200);
|
||||
--toaster-info-bg: var(--color-blue-200);
|
||||
--toaster-warning-bg: var(--color-yellow-200);
|
||||
--toaster-notification-bg: var(--color-neutral-150);
|
||||
|
||||
--card: var(--neutral-750);
|
||||
|
||||
/* ------------------- */
|
||||
|
@ -383,8 +415,6 @@ p {
|
|||
|
||||
--accent-foreground: oklch(0.985 0.002 247.839);
|
||||
|
||||
--destructive: oklch(0.704 0.191 22.216);
|
||||
|
||||
--border: oklch(1 0 0 / 10%);
|
||||
|
||||
--input: oklch(1 0 0 / 15%);
|
||||
|
|
|
@ -65,27 +65,27 @@ interface ToastInnerProps {
|
|||
|
||||
const variantConfig = {
|
||||
default: {
|
||||
bgColor: 'bg-neutral-150',
|
||||
bgColor: 'bg-toaster-default-bg',
|
||||
defaultIcon: 'Info',
|
||||
},
|
||||
success: {
|
||||
bgColor: 'bg-green-200',
|
||||
bgColor: 'bg-toaster-success-bg',
|
||||
defaultIcon: 'CheckCircle',
|
||||
},
|
||||
error: {
|
||||
bgColor: 'bg-red-200',
|
||||
bgColor: 'bg-toaster-error-bg',
|
||||
defaultIcon: 'XCircle',
|
||||
},
|
||||
info: {
|
||||
bgColor: 'bg-blue-200',
|
||||
bgColor: 'bg-toaster-info-bg',
|
||||
defaultIcon: 'Info',
|
||||
},
|
||||
warning: {
|
||||
bgColor: 'bg-yellow-200',
|
||||
bgColor: 'bg-toaster-warning-bg',
|
||||
defaultIcon: 'AlertTriangle',
|
||||
},
|
||||
notification: {
|
||||
bgColor: 'bg-neutral-150',
|
||||
bgColor: 'bg-toaster-notification-bg',
|
||||
defaultIcon: 'BellRing',
|
||||
},
|
||||
};
|
||||
|
@ -127,14 +127,16 @@ export const ToastInner: React.FC<ToastInnerProps> = ({
|
|||
>
|
||||
{variant !== 'default' && (
|
||||
<div className='flex items-center justify-center'>
|
||||
<Icon size={40} />
|
||||
<Icon className='text-text-alt' size={40} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Text Content */}
|
||||
<div className='grid gap-1'>
|
||||
<h6>{title}</h6>
|
||||
{description && <Label>{description}</Label>}
|
||||
<h6 className='text-text-alt'>{title}</h6>
|
||||
{description && (
|
||||
<Label className='text-text-alt'>{description}</Label>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Action Button */}
|
||||
|
|
|
@ -26,6 +26,8 @@ const buttonVariants = cva(
|
|||
'border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 w-32 justify-between font-normal',
|
||||
ghost:
|
||||
'hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50',
|
||||
destructive:
|
||||
'bg-destructive text-text shadow-xs hover:bg-hover-destructive active:bg-active-destructive disabled:bg-disabled-destructive',
|
||||
},
|
||||
size: {
|
||||
default: 'h-9 px-4 py-2 has-[>svg]:px-3',
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue