Skip to content

Commit f51155c

Browse files
authored
Merge pull request #47 from choosenname/29-improve-the-admin-panel
29 improve the admin panel
2 parents 50897dd + f7f84b9 commit f51155c

File tree

12 files changed

+546
-20
lines changed

12 files changed

+546
-20
lines changed

actions/register.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export const register = async (values: z.infer<typeof RegisterSchema>) => {
1414
return { error: "Invalid fields!" };
1515
}
1616

17-
const {name , password,departmentId } = validatedFields.data;
17+
const {name , password,departmentId , post, office, startWork, endWork} = validatedFields.data;
1818
const hashedPassword = await bcrypt.hash(password, 10);
1919

2020
const existingUser = await getUserByName(name);
@@ -29,6 +29,10 @@ export const register = async (values: z.infer<typeof RegisterSchema>) => {
2929
password: hashedPassword,
3030
departmentId,
3131
imageUrl: "",
32+
post,
33+
office,
34+
startWork,
35+
endWork,
3236
}
3337
});
3438

app/api/profile/route.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,14 @@ export async function PATCH(
3737
console.log("[PROFILE_PATCH]", error);
3838
return new NextResponse("Internal Error", { status: 500 });
3939
}
40+
}
41+
42+
export async function GET() {
43+
try {
44+
const user = await currentUser();
45+
return NextResponse.json(user);
46+
} catch (error) {
47+
console.log("[PROFILE_GET]", error);
48+
return new NextResponse("Internal Error", { status: 500 });
49+
}
4050
}

columns.tsx

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,14 @@ import {Server} from "@prisma/client";
1818
import {DeleteServerMenuItem} from "@/components/admin/items/delete-server-item";
1919
import axios from "axios";
2020

21+
const formatTime = (dateString: string) => {
22+
const date = new Date(dateString);
23+
return new Intl.DateTimeFormat('ru-RU', {
24+
hour: '2-digit',
25+
minute: '2-digit'
26+
}).format(date);
27+
}
28+
2129
export const UserColumn: ColumnDef<FullUserType>[] = [
2230
{
2331
accessorKey: "imageUrl",
@@ -29,15 +37,33 @@ export const UserColumn: ColumnDef<FullUserType>[] = [
2937
},
3038
{
3139
accessorKey: "name",
32-
header: "Name",
40+
header: "Имя",
3341
},
3442
{
3543
accessorKey: "role",
36-
header: "Role",
44+
header: "Роль",
3745
},
3846
{
3947
accessorKey: "department.department",
40-
header: "Department",
48+
header: "Отдел",
49+
},
50+
{
51+
accessorKey: "post",
52+
header: "Должность",
53+
},
54+
{
55+
accessorKey: "office",
56+
header: "Кабинет",
57+
},
58+
{
59+
accessorKey: "startWork",
60+
header: "Время начала работы",
61+
cell: ({ row }) => formatTime(row.getValue("startWork") as string),
62+
},
63+
{
64+
accessorKey: "endWork",
65+
header: "Время конца работы",
66+
cell: ({ row }) => formatTime(row.getValue("endWork") as string),
4167
},
4268
{
4369
id: "actions",
@@ -48,16 +74,16 @@ export const UserColumn: ColumnDef<FullUserType>[] = [
4874
<DropdownMenu>
4975
<DropdownMenuTrigger asChild>
5076
<Button variant="ghost" className="h-8 w-8 p-0">
51-
<span className="sr-only">Open menu</span>
77+
<span className="sr-only">Открыть меню</span>
5278
<MoreHorizontal className="h-4 w-4" />
5379
</Button>
5480
</DropdownMenuTrigger>
5581
<DropdownMenuContent align="end">
56-
<DropdownMenuLabel>Actions</DropdownMenuLabel>
82+
<DropdownMenuLabel>Действия</DropdownMenuLabel>
5783
<DropdownMenuItem
5884
onClick={() => navigator.clipboard.writeText(original.id)}
5985
>
60-
Copy user ID
86+
Копировать ID пользователя
6187
</DropdownMenuItem>
6288
<DropdownMenuItem
6389
onClick={() => axios.delete(`/api/profile/${original.id}`)}
@@ -84,11 +110,11 @@ export const ServerColumn: ColumnDef<FullServerType>[] = [
84110
},
85111
{
86112
accessorKey: "name",
87-
header: "Name",
113+
header: "Имя",
88114
},
89115
{
90116
accessorKey: "department.department",
91-
header: "Department",
117+
header: "Отдел",
92118
},
93119
{
94120
id: "actions",
@@ -100,16 +126,16 @@ export const ServerColumn: ColumnDef<FullServerType>[] = [
100126
<DropdownMenu>
101127
<DropdownMenuTrigger asChild>
102128
<Button variant="ghost" className="h-8 w-8 p-0">
103-
<span className="sr-only">Open menu</span>
129+
<span className="sr-only">Открыть меню</span>
104130
<MoreHorizontal className="h-4 w-4" />
105131
</Button>
106132
</DropdownMenuTrigger>
107133
<DropdownMenuContent align="end">
108-
<DropdownMenuLabel>Actions</DropdownMenuLabel>
134+
<DropdownMenuLabel>Действия</DropdownMenuLabel>
109135
<DropdownMenuItem
110136
onClick={() => navigator.clipboard.writeText(original.id)}
111137
>
112-
Copy server ID
138+
Копировать ID сервера
113139
</DropdownMenuItem>
114140
<DropdownMenuSeparator />
115141
<DeleteServerMenuItem original={server} />

components/admin/admin-sidebar.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ export const AdminSidebar = async () => {
1818
</div>
1919
<div className="px-3">
2020
<Separator className="bg-zinc-200 dark:bg-zinc-700 rounded-md my-2"/>
21-
<AdminSection label={"Users"} icon={<Users/>} link={"users"}/>
22-
<AdminSection label={"Servers"} icon={<Server/>} link={"servers"}/>
21+
<AdminSection label={"Сотрудники"} icon={<Users/>} link={"users"}/>
22+
<AdminSection label={"Сервера"} icon={<Server/>} link={"servers"}/>
2323
</div>
2424
</div>)
2525
}

components/auth/register-form.tsx

Lines changed: 73 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import {Select, SelectContent, SelectItem, SelectTrigger, SelectValue} from "@/c
2424
import {Department} from "@prisma/client";
2525
import axios from "axios";
2626
import {inviteUser} from "@/actions/invite";
27+
import {TimePickerDemo} from "@/components/time-picker/time-picker-demo";
2728

2829
export const RegisterForm = () => {
2930
const [error, setError] = useState<string | undefined>("");
@@ -37,6 +38,10 @@ export const RegisterForm = () => {
3738
name: "",
3839
password: "",
3940
departmentId: "",
41+
post: "",
42+
office: "",
43+
startWork: new Date(),
44+
endWork: new Date(),
4045
},
4146
});
4247

@@ -83,7 +88,7 @@ export const RegisterForm = () => {
8388
name="name"
8489
render={({ field }) => (
8590
<FormItem>
86-
<FormLabel>Name</FormLabel>
91+
<FormLabel>Имя</FormLabel>
8792
<FormControl>
8893
<Input
8994
{...field}
@@ -100,7 +105,7 @@ export const RegisterForm = () => {
100105
name="password"
101106
render={({ field }) => (
102107
<FormItem>
103-
<FormLabel>Password</FormLabel>
108+
<FormLabel>Пароль</FormLabel>
104109
<FormControl>
105110
<Input
106111
{...field}
@@ -113,6 +118,72 @@ export const RegisterForm = () => {
113118
</FormItem>
114119
)}
115120
/>
121+
<FormField
122+
control={form.control}
123+
name="post"
124+
render={({ field }) => (
125+
<FormItem>
126+
<FormLabel>Должность</FormLabel>
127+
<FormControl>
128+
<Input
129+
{...field}
130+
disabled={isPending}
131+
placeholder=""
132+
/>
133+
</FormControl>
134+
<FormMessage />
135+
</FormItem>
136+
)}
137+
/>
138+
<FormField
139+
control={form.control}
140+
name="office"
141+
render={({ field }) => (
142+
<FormItem>
143+
<FormLabel>Кабинет</FormLabel>
144+
<FormControl>
145+
<Input
146+
{...field}
147+
disabled={isPending}
148+
placeholder=""
149+
/>
150+
</FormControl>
151+
<FormMessage />
152+
</FormItem>
153+
)}
154+
/>
155+
<FormField
156+
control={form.control}
157+
name="startWork"
158+
render={({ field }) => (
159+
<FormItem>
160+
<FormLabel>Время начала работы</FormLabel>
161+
<FormControl>
162+
<TimePickerDemo
163+
setDate={field.onChange}
164+
date={field.value}
165+
/>
166+
</FormControl>
167+
<FormMessage />
168+
</FormItem>
169+
)}
170+
/>
171+
<FormField
172+
control={form.control}
173+
name="endWork"
174+
render={({ field }) => (
175+
<FormItem>
176+
<FormLabel>Время конца работы</FormLabel>
177+
<FormControl>
178+
<TimePickerDemo
179+
setDate={field.onChange}
180+
date={field.value}
181+
/>
182+
</FormControl>
183+
<FormMessage />
184+
</FormItem>
185+
)}
186+
/>
116187
<FormField
117188
control={form.control}
118189
name="departmentId"

components/auth/user-button.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export const UserButton = ({user}: UserButtonProps) => {
2828
<UserAvatar src={user.imageUrl}/>
2929
</DropdownMenuTrigger>
3030
<DropdownMenuContent className="w-40" align="end">
31-
<Button variant="ghost" onClick={() => onOpen("editProfile", {user})}>
31+
<Button variant="ghost" onClick={() => onOpen("editProfile")}>
3232
<DropdownMenuItem>
3333
<Settings className="h-4 w-4 mr-2"/>
3434
Профиль

components/modals/edit-profile-modal.tsx

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import axios from "axios";
44
import * as z from "zod";
55
import { zodResolver } from "@hookform/resolvers/zod";
66
import { useForm } from "react-hook-form";
7-
import { useEffect } from "react";
7+
import {useEffect, useState} from "react";
88

99
import {
1010
Dialog,
@@ -27,12 +27,28 @@ import { FileUpload } from "@/components/files/file-upload";
2727
import { useRouter } from "next/navigation";
2828
import { useModal } from "@/hooks/use-modal-store";
2929
import { ProfileSchema } from "@/schemas";
30+
import {User} from "@prisma/client";
3031

3132
export const EditProfileModal = () => {
3233
const { isOpen, onClose, type, data } = useModal();
3334
useRouter();
3435
const isModalOpen = isOpen && type === "editProfile";
35-
const { user } = data;
36+
const [user, setUser] = useState<User | null>(null);
37+
38+
useEffect(() => {
39+
const fetchUser = async () => {
40+
try {
41+
const response = await axios.get('/api/profile');
42+
setUser(response.data);
43+
} catch (error) {
44+
console.error('Failed to fetch user:', error);
45+
}
46+
};
47+
48+
if (isModalOpen) {
49+
fetchUser();
50+
}
51+
}, [isModalOpen]);
3652

3753
const form = useForm({
3854
resolver: zodResolver(ProfileSchema),
@@ -46,7 +62,7 @@ export const EditProfileModal = () => {
4662
useEffect(() => {
4763
if (user) {
4864
form.setValue("name", user.name || "");
49-
form.setValue("password", user.password || "");
65+
form.setValue("password", "");
5066
form.setValue("imageUrl", user.imageUrl);
5167
}
5268
}, [user, form]);
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
"use client";
2+
3+
import * as React from "react";
4+
import { Clock } from "lucide-react";
5+
import { Label } from "@/components/ui/label";
6+
import { TimePickerInput } from "./time-picker-input";
7+
8+
interface TimePickerDemoProps {
9+
date: Date | undefined;
10+
setDate: (date: Date | undefined) => void;
11+
}
12+
13+
export function TimePickerDemo({ date, setDate }: TimePickerDemoProps) {
14+
const minuteRef = React.useRef<HTMLInputElement>(null);
15+
const hourRef = React.useRef<HTMLInputElement>(null);
16+
17+
return (
18+
<div className="flex items-end gap-2">
19+
<div className="grid gap-1 text-center">
20+
<Label htmlFor="hours" className="text-xs">
21+
Hours
22+
</Label>
23+
<TimePickerInput
24+
picker="hours"
25+
date={date}
26+
setDate={setDate}
27+
ref={hourRef}
28+
onRightFocus={() => minuteRef.current?.focus()}
29+
/>
30+
</div>
31+
<div className="grid gap-1 text-center">
32+
<Label htmlFor="minutes" className="text-xs">
33+
Minutes
34+
</Label>
35+
<TimePickerInput
36+
picker="minutes"
37+
date={date}
38+
setDate={setDate}
39+
ref={minuteRef}
40+
onLeftFocus={() => hourRef.current?.focus()}
41+
/>
42+
</div>
43+
<div className="flex h-10 items-center">
44+
<Clock className="ml-2 h-4 w-4" />
45+
</div>
46+
</div>
47+
);
48+
}

0 commit comments

Comments
 (0)