feat(frontend): add async admin form foundations

This commit is contained in:
2026-06-14 00:04:22 +03:30
parent 25bd46ea2a
commit 9080b0caea
9 changed files with 668 additions and 80 deletions

View File

@@ -1,7 +1,6 @@
"use client";
import { useEffect, useMemo, useState } from "react";
import { useQuery } from "@tanstack/react-query";
import { useCallback, useEffect, useMemo, useState } from "react";
import {
AlertTriangle,
ArrowRight,
@@ -10,7 +9,7 @@ import {
MessageSquareMore,
Smartphone,
} from "lucide-react";
import SearchableCombobox from "@/components/SearchableCombobox";
import AsyncSearchableCombobox from "@/components/AsyncSearchableCombobox";
import OtpCodeField from "@/components/OtpCodeField";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
@@ -96,25 +95,23 @@ export default function Auth() {
return () => window.clearInterval(timer);
}, []);
const { data: majors = [], isLoading: majorsLoading } = useQuery({
queryKey: ["majors"],
queryFn: () => api.getMajors(),
staleTime: 7 * 24 * 60 * 60 * 1000,
});
const { data: universities = [], isLoading: universitiesLoading } = useQuery({
queryKey: ["universities"],
queryFn: () => api.getUniversities(),
staleTime: 7 * 24 * 60 * 60 * 1000,
});
const loadMajors = useCallback(async (params: { search: string; limit: number; offset: number }) => {
const data = await api.getMajorsPaged(params);
return {
count: data.count,
results: data.results.map((major) => ({ value: String(major.code), label: major.label })),
};
}, []);
const majorItems = useMemo(
() => majors.map((major) => ({ value: String(major.code), label: major.label })),
[majors],
);
const universityItems = useMemo(
() => universities.map((university) => ({ value: String(university.code), label: university.label })),
[universities],
);
const loadUniversities = useCallback(async (params: { search: string; limit: number; offset: number }) => {
const data = await api.getUniversitiesPaged(params);
return {
count: data.count,
results: data.results.map((university) => ({ value: String(university.code), label: university.label })),
};
}, []);
const majorsLoading = false;
const universitiesLoading = false;
const stepMeta = useMemo(() => {
switch (step) {
@@ -666,14 +663,14 @@ export default function Auth() {
{universitiesLoading ? (
<div className="h-12 animate-pulse rounded-2xl bg-muted" />
) : (
<SearchableCombobox
items={universityItems}
<AsyncSearchableCombobox
loadOptions={loadUniversities}
value={registerForm.university}
onChange={(value) => updateRegisterForm("university", value)}
placeholder="انتخاب دانشگاه"
searchPlaceholder="نام دانشگاه را بنویسید..."
emptyText="دانشگاهی پیدا نشد"
dir="rtl"
className="h-12 rounded-2xl"
/>
)}
</div>
@@ -684,14 +681,14 @@ export default function Auth() {
{majorsLoading ? (
<div className="h-12 animate-pulse rounded-2xl bg-muted" />
) : (
<SearchableCombobox
items={majorItems}
<AsyncSearchableCombobox
loadOptions={loadMajors}
value={registerForm.major}
onChange={(value) => updateRegisterForm("major", value)}
placeholder="انتخاب رشته"
searchPlaceholder="نام رشته را بنویسید..."
emptyText="رشته‌ای پیدا نشد"
dir="rtl"
className="h-12 rounded-2xl"
/>
)}
</div>