Skip to content
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

fixed README.md #3

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 108 additions & 0 deletions apps/web/app/home/_components/add-student-dialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
'use client';

import { useEffect, useState } from 'react';
import { useActionState } from 'react';

import { Button } from '@kit/ui/button';
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from '@kit/ui/dialog';
import { Input } from '@kit/ui/input';
import { Label } from '@kit/ui/label';

import addStudent from '~/home/actions';

type State = {
errors?: {
name?: string[];
email?: string[];
};
message?: string | null;
success?: boolean;
};

const initialState: State = { message: null, errors: {}, success: false };

export default function AddStudentDialog() {
const [open, setOpen] = useState(false);
const [state, formAction] = useActionState(addStudent, initialState);

useEffect(() => {
if (state.success) {
setOpen(false);
}
}, [state.success]);

return (
<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild>
<Button variant="outline">Add Student</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-[425px]">
<DialogHeader>
<DialogTitle>Add Student</DialogTitle>
<DialogDescription>
Add a new student to your account.
</DialogDescription>
</DialogHeader>
<form action={formAction}>
<div className="grid gap-4 py-4">
<div className="grid grid-cols-4 items-center gap-4">
<Label htmlFor="name" className="text-right">
Name
</Label>
<div className="col-span-3">
<Input
id="name"
name="name"
placeholder="Enter student name"
className={state.errors?.name ? 'border-red-500' : ''}
/>
{state.errors?.name && (
<p className="mt-1 text-sm text-red-500">
{state.errors.name[0]}
</p>
)}
</div>
</div>
<div className="grid grid-cols-4 items-center gap-4">
<Label htmlFor="email" className="text-right">
Email
</Label>
<div className="col-span-3">
<Input
id="email"
name="email"
type="email"
placeholder="[email protected]"
className={state.errors?.email ? 'border-red-500' : ''}
/>
{state.errors?.email && (
<p className="mt-1 text-sm text-red-500">
{state.errors.email[0]}
</p>
)}
</div>
</div>
</div>
{state.message && (
<p
className={`text-sm ${state.success ? 'text-green-500' : 'text-red-500'} mb-4`}
>
{state.message}
</p>
)}
<DialogFooter>
<Button type="submit">Add Student</Button>
</DialogFooter>
</form>
</DialogContent>
</Dialog>
);
}
80 changes: 80 additions & 0 deletions apps/web/app/home/_components/student-table.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { getSupabaseServerClient } from '@kit/supabase/server-client';
import {
Table,
TableBody,
TableCaption,
TableCell,
TableFooter,
TableHead,
TableHeader,
TableRow,
} from '@kit/ui/table';

import AddStudentDialog from '~/home/_components/add-student-dialog';
import { addStudent } from '~/home/actions';

export default async function StudentTable() {
const client = getSupabaseServerClient();
const { data, error } = await client.from('student').select('*');

if (error) {
return (
<div className="my-4 rounded-md bg-red-50 p-4">
<div className="flex">
<div className="text-sm text-red-700">
Failed to load students. Please try again later.
</div>
</div>
</div>
);
}

return (
<div className="rounded-md border">
<Table>
<TableCaption>List of registered students in your account</TableCaption>
<TableHeader>
<TableRow>
<TableHead>Name</TableHead>
<TableHead>Email</TableHead>
<TableHead className="hidden md:table-cell">Account ID</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{data.length === 0 ? (
<TableRow>
<TableCell
colSpan={4}
className="text-center text-muted-foreground"
>
No students found. Add your first student to get started.
</TableCell>
</TableRow>
) : (
data.map((student) => (
<TableRow key={student.student_id}>
<TableCell className="font-medium">{student.name}</TableCell>
<TableCell>{student.email}</TableCell>
<TableCell className="hidden md:table-cell">
{student.account_id}
</TableCell>
<TableCell className="text-right"></TableCell>
</TableRow>
))
)}
</TableBody>
<TableFooter>
<TableRow>
<TableCell colSpan={3}>Total Students</TableCell>
<TableCell className="text-right font-medium">
{data.length}
</TableCell>
</TableRow>
</TableFooter>
</Table>
<div className="p-4">
<AddStudentDialog />
</div>
</div>
);
}
57 changes: 57 additions & 0 deletions apps/web/app/home/actions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
'use server';

import { revalidatePath } from 'next/cache';

import { z } from 'zod';

import { getSupabaseServerClient } from '@kit/supabase/server-client';

type State = {
errors?: {
name?: string[];
email?: string[];
};
message?: string | null;
success?: boolean;
};

export default async function addStudent(prevState: State, formData: FormData) {
const studentSchema = z.object({
name: z.string().min(2, 'Name is required'),
email: z.string().email('Valid email is required'),
});

const validateData = studentSchema.safeParse({
name: formData.get('name'),
email: formData.get('email'),
});

if (!validateData.success) {
return {
errors: validateData.error.flatten().fieldErrors,
message: 'Please fix the errors above',
success: false,
};
}

const client = getSupabaseServerClient();
const { data: userData } = await client.auth.getUser();

const { error } = await client.from('student').insert({
...validateData.data,
account_id: userData.user?.id,
});

if (error) {
return {
message: 'Failed to add student',
success: false,
};
}

revalidatePath('/home');
return {
message: 'Student added successfully',
success: true,
};
}
3 changes: 2 additions & 1 deletion apps/web/app/home/page.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { PageBody, PageHeader } from '@kit/ui/page';

import { DashboardDemo } from '~/home/_components/dashboard-demo';
import StudentTable from '~/home/_components/student-table';

export default function HomePage() {
return (
<>
<PageHeader title={'Dashboard'} description={'Your SaaS at a glance'} />

<PageBody>
<StudentTable />
<DashboardDemo />
</PageBody>
</>
Expand Down
Loading