From 0dc17177044eec615c4fe0f69db61915db93417e Mon Sep 17 00:00:00 2001 From: vergiLgood1 Date: Tue, 11 Mar 2025 01:05:03 +0700 Subject: [PATCH] menggunakan refect function untuk mendapatkan data terbaru --- .../(protected)/(admin)/dashboard/page.tsx | 2 + .../user-management/_components/sheet.tsx | 24 +- .../_components/update-user.tsx | 11 +- .../_components/user-management.tsx | 3 +- .../dashboard/user-management/action.ts | 27 ++- .../app/_components/date-time-picker.tsx | 106 +++------ .../app/_components/form-wrapper.tsx | 2 + .../app/_components/ui/date-picker.tsx | 221 +++++++++++++----- .../providers/react-query-provider.tsx | 2 +- sigap-website/src/models/users/users.model.ts | 1 - 10 files changed, 225 insertions(+), 174 deletions(-) diff --git a/sigap-website/app/(protected)/(admin)/dashboard/page.tsx b/sigap-website/app/(protected)/(admin)/dashboard/page.tsx index afc58de..118a272 100644 --- a/sigap-website/app/(protected)/(admin)/dashboard/page.tsx +++ b/sigap-website/app/(protected)/(admin)/dashboard/page.tsx @@ -1,3 +1,4 @@ +import { DateTimePicker2 } from "@/app/_components/ui/date-picker"; import { createClient } from "@/utils/supabase/server"; import { redirect } from "next/navigation"; @@ -21,6 +22,7 @@ export default async function DashboardPage() {
               {JSON.stringify(user, null, 2)}
             
+
diff --git a/sigap-website/app/(protected)/(admin)/dashboard/user-management/_components/sheet.tsx b/sigap-website/app/(protected)/(admin)/dashboard/user-management/_components/sheet.tsx index f485b2c..21dbb32 100644 --- a/sigap-website/app/(protected)/(admin)/dashboard/user-management/_components/sheet.tsx +++ b/sigap-website/app/(protected)/(admin)/dashboard/user-management/_components/sheet.tsx @@ -147,22 +147,6 @@ export function UserDetailSheet({ }, }); - const handleDeleteUser = () => { - deleteUserMutation.mutate(); - }; - - const handleSendPasswordRecovery = () => { - sendPasswordRecoveryMutation.mutate(); - }; - - const handleSendMagicLink = () => { - sendMagicLinkMutation.mutate(); - }; - - const handleToggleBan = () => { - toggleBanMutation.mutate(); - }; - const handleCopyItem = (item: string) => { navigator.clipboard.writeText(item); toast.success("Copied to clipboard"); @@ -300,7 +284,7 @@ export function UserDetailSheet({
diff --git a/sigap-website/app/(protected)/(admin)/dashboard/user-management/action.ts b/sigap-website/app/(protected)/(admin)/dashboard/user-management/action.ts index 51c05e8..e26d50d 100644 --- a/sigap-website/app/(protected)/(admin)/dashboard/user-management/action.ts +++ b/sigap-website/app/(protected)/(admin)/dashboard/user-management/action.ts @@ -163,12 +163,11 @@ export async function updateUser( const { data, error } = await supabase.auth.admin.updateUserById(userId, { email: params.email, email_confirm: params.email_confirmed_at, - password: params.password_hash, - password_hash: params.password_hash, + password: params.password_hash ?? undefined, + password_hash: params.password_hash ?? undefined, phone: params.phone, phone_confirm: params.phone_confirmed_at, role: params.role, - ban_duration: params.banned_until, user_metadata: params.user_metadata, app_metadata: params.app_metadata, }); @@ -317,6 +316,28 @@ export async function unbanUser(userId: string): Promise { throw new Error(error.message); } + const user = await db.users.findUnique({ + where: { + id: userId, + }, + select: { + banned_until: true, + } + }) + + if (!user) { + throw new Error("User not found"); + } + + // const updateUser = await db.users.update({ + // where: { + // id: userId, + // }, + // data: { + // banned_until: null, + // }, + // }) + return { data: { user: data.user, diff --git a/sigap-website/app/_components/date-time-picker.tsx b/sigap-website/app/_components/date-time-picker.tsx index dc954db..2fdfed9 100644 --- a/sigap-website/app/_components/date-time-picker.tsx +++ b/sigap-website/app/_components/date-time-picker.tsx @@ -1,5 +1,3 @@ - - import type React from "react" import { useEffect, useMemo, useState, useCallback, useRef } from "react" @@ -9,6 +7,7 @@ import { useVirtualizer } from "@tanstack/react-virtual" import { cn } from "@/lib/utils" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/app/_components/ui/select" +import { Input } from "@/app/_components/ui/input" import { Button } from "@/app/_components/ui/button" // Custom calendar component with enhanced year and month navigation @@ -23,15 +22,15 @@ export function DateTimePicker({ minuteStep = 1, showSeconds = true, }: { - selected?: Date - onSelect: (date?: Date) => void - disabled?: (date: Date) => boolean - fromYear?: number - toYear?: number - showTimePicker?: boolean - className?: string - minuteStep?: number - showSeconds?: boolean + selected?: Date + onSelect: (date?: Date) => void + disabled?: (date: Date) => boolean + fromYear?: number + toYear?: number + showTimePicker?: boolean + className?: string + minuteStep?: number + showSeconds?: boolean }) { // Initialize with selected date or current date const [date, setDate] = useState(() => { @@ -84,20 +83,18 @@ export function DateTimePicker({ if (date) { const newDate = new Date(date) - const newHours = Number.parseInt(hours, 10) - const newMinutes = Number.parseInt(minutes, 10) - const newSeconds = Number.parseInt(seconds, 10) - - newDate.setHours(newHours, newMinutes, newSeconds, 0) + newDate.setHours(Number.parseInt(hours, 10)) + newDate.setMinutes(Number.parseInt(minutes, 10)) + newDate.setSeconds(Number.parseInt(seconds, 10)) + newDate.setMilliseconds(0) // Only call onSelect if the date actually changed if (!selected || Math.abs(newDate.getTime() - (selected?.getTime() || 0)) > 100) { isUpdatingRef.current = true onSelect(newDate) - // Use requestAnimationFrame instead of setTimeout for better performance - requestAnimationFrame(() => { + setTimeout(() => { isUpdatingRef.current = false - }) + }, 0) } } else { onSelect(undefined) @@ -117,13 +114,7 @@ export function DateTimePicker({ }, [selected]) // Generate years array from fromYear to toYear - const years = useMemo(() => { - const yearsArray = [] - for (let i = toYear; i >= fromYear; i--) { - yearsArray.push(i) - } - return yearsArray - }, [fromYear, toYear]) + const years = useMemo(() => Array.from({ length: toYear - fromYear + 1 }, (_, i) => toYear - i), [fromYear, toYear]) const months = useMemo( () => [ @@ -212,7 +203,6 @@ export function DateTimePicker({ ({ displayMonth }: { displayMonth: Date }) => { const month = displayMonth.getMonth() const year = displayMonth.getFullYear() - const yearListRef = useRef(null) const handleMonthChange = useCallback( (newMonth: string) => { @@ -221,7 +211,7 @@ export function DateTimePicker({ newDate.setMonth(monthIndex) setDate(newDate) }, - [date], + [date, months], ) const handleYearChange = useCallback( @@ -233,25 +223,6 @@ export function DateTimePicker({ [date], ) - // Create a virtualizer for the years list - const virtualizer = useVirtualizer({ - count: years.length, - getScrollElement: () => yearListRef.current, - estimateSize: () => 36, // Approximate height of each year item - overscan: 5, // Reduced overscan for better performance - }) - - // Pre-scroll to current year when the select opens - const handleYearSelectOpen = useCallback(() => { - const yearIndex = years.findIndex((y) => y === year) - if (yearIndex !== -1 && yearListRef.current) { - // Use requestAnimationFrame for smoother scrolling - requestAnimationFrame(() => { - virtualizer.scrollToIndex(yearIndex, { align: "center" }) - }) - } - }, [virtualizer, years, year]) - return (
- - e.preventDefault()} - > -
- {virtualizer.getVirtualItems().map((virtualItem) => ( - - {years[virtualItem.index]} - - ))} -
+ + {years.map((yearValue) => ( + + {yearValue} + + ))}
@@ -413,5 +360,4 @@ export function DateTimePicker({
) -} - +} \ No newline at end of file diff --git a/sigap-website/app/_components/form-wrapper.tsx b/sigap-website/app/_components/form-wrapper.tsx index d080681..cc941bf 100644 --- a/sigap-website/app/_components/form-wrapper.tsx +++ b/sigap-website/app/_components/form-wrapper.tsx @@ -14,6 +14,7 @@ import { Switch } from "@/app/_components/ui/switch" import { Checkbox } from "@/app/_components/ui/checkbox" import { DayPicker } from "react-day-picker" import { DateTimePicker } from "@/app/_components/date-time-picker" +import { DateTimePicker2 } from "./ui/date-picker" // Reusable form field component to reduce repetition interface FormFieldProps { @@ -127,6 +128,7 @@ export function FormFieldWrapper({ toYear={toYear} showTimePicker /> + ) : rows > 1 ? ( diff --git a/sigap-website/app/_components/ui/date-picker.tsx b/sigap-website/app/_components/ui/date-picker.tsx index 31acdc1..3cdbace 100644 --- a/sigap-website/app/_components/ui/date-picker.tsx +++ b/sigap-website/app/_components/ui/date-picker.tsx @@ -1,8 +1,8 @@ "use client" import * as React from "react" -import { format, getMonth, getYear, setMonth, setYear } from "date-fns" -import { Calendar as CalendarIcon } from "lucide-react" +import { format, getMonth, getYear, setMonth, setYear, setHours, setMinutes, setSeconds } from "date-fns" +import { Calendar as CalendarIcon, Clock } from "lucide-react" import { cn } from "@/lib/utils" import { Button } from "@/app/_components/ui/button" @@ -13,37 +13,35 @@ import { PopoverTrigger, } from "@/app/_components/ui/popover" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./select" +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/app/_components/ui/tabs" -interface DatePickerProps { +interface DateTimePickerProps { startYear?: number; endYear?: number; } -export function DatePicker({ + +export function DateTimePicker2({ startYear = getYear(new Date()) - 100, endYear = getYear(new Date()) + 100, -}: DatePickerProps) { - +}: DateTimePickerProps) { const [date, setDate] = React.useState(new Date()); const months = [ - 'January', - 'February', - 'March', - 'April', - 'May', - 'June', - 'July', - 'August', - 'September', - 'October', - 'November', - 'December', + 'January', 'February', 'March', 'April', 'May', 'June', + 'July', 'August', 'September', 'October', 'November', 'December', ]; + const years = Array.from( { length: endYear - startYear + 1 }, (_, i) => startYear + i ); + // Generate hours (0-23) + const hours = Array.from({ length: 24 }, (_, i) => i); + + // Generate minutes and seconds (0-59) + const minutesSeconds = Array.from({ length: 60 }, (_, i) => i); + const handleMonthChange = (month: string) => { const newDate = setMonth(date, months.indexOf(month)); setDate(newDate); @@ -51,67 +49,170 @@ export function DatePicker({ const handleYearChange = (year: string) => { const newDate = setYear(date, parseInt(year)); - setDate(newDate) + setDate(newDate); } - const handleSelect = (selectedData: Date | undefined) => { - if (selectedData) { - setDate(selectedData) + const handleHourChange = (hour: string) => { + const newDate = setHours(date, parseInt(hour)); + setDate(newDate); + } + + const handleMinuteChange = (minute: string) => { + const newDate = setMinutes(date, parseInt(minute)); + setDate(newDate); + } + + const handleSecondChange = (second: string) => { + const newDate = setSeconds(date, parseInt(second)); + setDate(newDate); + } + + const handleSelect = (selectedDate: Date | undefined) => { + if (selectedDate) { + // Preserve time when changing date + const newDate = new Date(selectedDate); + newDate.setHours(date.getHours(), date.getMinutes(), date.getSeconds()); + setDate(newDate); } } + // Format number to always have 2 digits (e.g., 1 -> 01) + const formatTwoDigits = (num: number) => num.toString().padStart(2, '0'); + return ( -
- - -
+ + + + + Date + + + + Time + + - + +
+ + +
+ + +
+ + +
+
+ {format(date, "HH:mm:ss")} +
+ +
+
+ + +
+ +
+ + +
+ +
+ + +
+
+
+
+
) diff --git a/sigap-website/providers/react-query-provider.tsx b/sigap-website/providers/react-query-provider.tsx index 770cae5..382821d 100644 --- a/sigap-website/providers/react-query-provider.tsx +++ b/sigap-website/providers/react-query-provider.tsx @@ -13,7 +13,7 @@ const ReactQueryProvider = ({ children }: { children: React.ReactNode }) => { // Pengaturan caching global staleTime: 5 * 60 * 1000, gcTime: 10 * 60 * 1000, - refetchOnWindowFocus: false, + refetchOnWindowFocus: true, retry: 1, retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000), // Exponential backoff diff --git a/sigap-website/src/models/users/users.model.ts b/sigap-website/src/models/users/users.model.ts index 3df4dcb..fbc39f3 100644 --- a/sigap-website/src/models/users/users.model.ts +++ b/sigap-website/src/models/users/users.model.ts @@ -106,7 +106,6 @@ export const UpdateUserParamsSchema = z.object({ created_at: z.union([z.string(), z.date()]).optional(), updated_at: z.union([z.string(), z.date()]).optional(), is_anonymous: z.boolean().optional(), - banned_until: z.string().optional(), user_metadata: z.record(z.any()).optional(), app_metadata: z.record(z.any()).optional(), profile: z