@goplusvn/core 0.1.46 → 0.1.48
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -1
- package/src/auth/__tests__/api-handler.test.ts +141 -0
- package/src/auth/api-handler.ts +141 -0
- package/src/rbac/pages/action-list-page.tsx +181 -256
- package/src/rbac/pages/index.ts +5 -0
- package/src/rbac/pages/lib/action-api.ts +38 -0
- package/src/rbac/pages/lib/group-taxonomy.ts +110 -0
- package/src/rbac/pages/lib/rbac-page-shell.tsx +148 -0
- package/src/rbac/pages/resource-list-page.tsx +350 -499
- package/src/schemas/branch.schema.ts +35 -0
- package/src/schemas/user.schema.ts +2 -0
- package/src/schemas/warehouse.schema.ts +2 -0
- package/src/user/components/unified-profile-dialog.tsx +81 -81
- package/src/user/components/users-card-view.tsx +12 -12
|
@@ -44,6 +44,41 @@ export const branchSchema = z.object({
|
|
|
44
44
|
.nullable()
|
|
45
45
|
.or(z.literal("")),
|
|
46
46
|
|
|
47
|
+
position: z
|
|
48
|
+
.string()
|
|
49
|
+
.max(255, "Chức vụ tối đa 255 ký tự")
|
|
50
|
+
.trim()
|
|
51
|
+
.optional()
|
|
52
|
+
.nullable(),
|
|
53
|
+
|
|
54
|
+
representativePhone: z
|
|
55
|
+
.string()
|
|
56
|
+
.max(20, "Số điện thoại tối đa 20 ký tự")
|
|
57
|
+
.trim()
|
|
58
|
+
.optional()
|
|
59
|
+
.nullable(),
|
|
60
|
+
|
|
61
|
+
citizenId: z
|
|
62
|
+
.string()
|
|
63
|
+
.max(50, "Số CCCD tối đa 50 ký tự")
|
|
64
|
+
.trim()
|
|
65
|
+
.optional()
|
|
66
|
+
.nullable(),
|
|
67
|
+
|
|
68
|
+
taxCode: z
|
|
69
|
+
.string()
|
|
70
|
+
.max(50, "Mã số thuế tối đa 50 ký tự")
|
|
71
|
+
.trim()
|
|
72
|
+
.optional()
|
|
73
|
+
.nullable(),
|
|
74
|
+
|
|
75
|
+
bankAccount: z
|
|
76
|
+
.string()
|
|
77
|
+
.max(255, "Số tài khoản tối đa 255 ký tự")
|
|
78
|
+
.trim()
|
|
79
|
+
.optional()
|
|
80
|
+
.nullable(),
|
|
81
|
+
|
|
47
82
|
status: z.enum(["active", "inactive"]).default("active"),
|
|
48
83
|
});
|
|
49
84
|
|
|
@@ -3,6 +3,8 @@ import { z } from "zod";
|
|
|
3
3
|
export const userSchema = z.object({
|
|
4
4
|
name: z.string().min(1, "Tên hiển thị là bắt buộc"),
|
|
5
5
|
email: z.string().email("Email không hợp lệ"),
|
|
6
|
+
phoneNumber: z.string().optional(),
|
|
7
|
+
address: z.string().optional(),
|
|
6
8
|
// Support both boolean and status string, normalize to boolean
|
|
7
9
|
isActive: z.preprocess((val) => {
|
|
8
10
|
if (typeof val === "boolean") return val;
|
|
@@ -32,6 +32,8 @@ export const warehouseSchema = z.object({
|
|
|
32
32
|
.or(z.literal(""))
|
|
33
33
|
.transform((val) => val || ""),
|
|
34
34
|
|
|
35
|
+
branchId: z.string().optional(),
|
|
36
|
+
|
|
35
37
|
status: z.preprocess((val) => {
|
|
36
38
|
// Hỗ trợ boolean từ switch field (true -> active, false -> inactive)
|
|
37
39
|
if (typeof val === "boolean") {
|
|
@@ -400,7 +400,7 @@ export function UnifiedProfileDialog({
|
|
|
400
400
|
|
|
401
401
|
return (
|
|
402
402
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
|
403
|
-
<DialogContent className="w-[100dvw] h-[100dvh] max-w-none max-h-none sm:w-auto sm:max-w-5xl sm:h-[85vh] flex flex-col p-0 gap-0 overflow-hidden border border-
|
|
403
|
+
<DialogContent className="w-[100dvw] h-[100dvh] max-w-none max-h-none sm:w-auto sm:max-w-5xl sm:h-[85vh] flex flex-col p-0 gap-0 overflow-hidden border border-border shadow-[0_12px_32px_rgba(0,0,0,0.1)] bg-background rounded-none sm:rounded-[12px] [&>button.absolute]:hidden">
|
|
404
404
|
<DialogHeader className="sr-only">
|
|
405
405
|
<DialogTitle>
|
|
406
406
|
{mode === "create" ? "Tạo mới" : "Chỉnh sửa"}
|
|
@@ -409,33 +409,33 @@ export function UnifiedProfileDialog({
|
|
|
409
409
|
|
|
410
410
|
<div className="flex flex-col w-full h-full overflow-hidden">
|
|
411
411
|
{/* TOP HEADER: Profile Info + Actions */}
|
|
412
|
-
<div className="shrink-0 flex flex-col border-b border-
|
|
412
|
+
<div className="shrink-0 flex flex-col border-b border-border bg-muted px-6 pt-5 pb-0">
|
|
413
413
|
<div className="flex flex-col sm:flex-row sm:items-start justify-between gap-4 pb-5">
|
|
414
414
|
{/* Profile Header */}
|
|
415
415
|
<div className="flex items-center gap-4">
|
|
416
416
|
<div className="relative">
|
|
417
|
-
<Avatar className="h-12 w-12 sm:h-14 sm:w-14 rounded-full border border-
|
|
417
|
+
<Avatar className="h-12 w-12 sm:h-14 sm:w-14 rounded-full border border-border">
|
|
418
418
|
<AvatarImage
|
|
419
419
|
src={displayData.avatar}
|
|
420
420
|
className="object-cover rounded-full"
|
|
421
421
|
/>
|
|
422
|
-
<AvatarFallback className="text-[16px] sm:text-[18px] font-semibold bg-
|
|
422
|
+
<AvatarFallback className="text-[16px] sm:text-[18px] font-semibold bg-primary text-primary-foreground rounded-full">
|
|
423
423
|
{getInitials(profileData.name)}
|
|
424
424
|
</AvatarFallback>
|
|
425
425
|
</Avatar>
|
|
426
426
|
<div
|
|
427
427
|
className={cn(
|
|
428
|
-
"absolute -bottom-0.5 -right-0.5 w-3.5 h-3.5 sm:w-4 sm:h-4 border-[2px] border-
|
|
429
|
-
isActive ? "bg-
|
|
428
|
+
"absolute -bottom-0.5 -right-0.5 w-3.5 h-3.5 sm:w-4 sm:h-4 border-[2px] border-muted rounded-full z-10",
|
|
429
|
+
isActive ? "bg-success-semantic" : "bg-text-tertiary"
|
|
430
430
|
)}
|
|
431
431
|
title={isActive ? "Hoạt động" : "Đã khóa"}
|
|
432
432
|
/>
|
|
433
433
|
</div>
|
|
434
434
|
<div>
|
|
435
|
-
<h2 className="text-[18px] sm:text-[20px] font-semibold leading-tight text-
|
|
435
|
+
<h2 className="text-[18px] sm:text-[20px] font-semibold leading-tight text-foreground">
|
|
436
436
|
{profileData.name || "Chưa đặt tên"}
|
|
437
437
|
</h2>
|
|
438
|
-
<p className="mt-1 text-[13px] text-
|
|
438
|
+
<p className="mt-1 text-[13px] text-muted-foreground">
|
|
439
439
|
{profileData.email || profileData.phone || "Chưa có thông tin liên hệ"}
|
|
440
440
|
</p>
|
|
441
441
|
</div>
|
|
@@ -455,7 +455,7 @@ export function UnifiedProfileDialog({
|
|
|
455
455
|
// dịch kèm `display:block` với độ ưu tiên (0,1,1) cao hơn `inline-flex`
|
|
456
456
|
// (0,1,0) của span icon+chữ bên trong, nên icon (svg Preflight display:block)
|
|
457
457
|
// bị đẩy xuống dòng riêng. `!inline-flex` (!important) thắng tuyệt đối.
|
|
458
|
-
className="shrink-0 h-9 min-h-0 whitespace-nowrap [&_span]:!inline-flex [&_span]:!items-center [&_span]:!gap-2 [&_span]:overflow-hidden [&_svg]:shrink-0 bg-
|
|
458
|
+
className="shrink-0 h-9 min-h-0 whitespace-nowrap [&_span]:!inline-flex [&_span]:!items-center [&_span]:!gap-2 [&_span]:overflow-hidden [&_svg]:shrink-0 bg-background border-input focus:ring-primary focus:border-primary rounded-md"
|
|
459
459
|
>
|
|
460
460
|
<SelectValue placeholder="Chọn loại" />
|
|
461
461
|
</SelectTrigger>
|
|
@@ -465,7 +465,7 @@ export function UnifiedProfileDialog({
|
|
|
465
465
|
className="inline-flex items-center gap-2"
|
|
466
466
|
style={{ whiteSpace: "nowrap" }}
|
|
467
467
|
>
|
|
468
|
-
<UserCog className="h-4 w-4 text-
|
|
468
|
+
<UserCog className="h-4 w-4 text-primary shrink-0" />
|
|
469
469
|
Nhân viên
|
|
470
470
|
</span>
|
|
471
471
|
</SelectItem>
|
|
@@ -474,7 +474,7 @@ export function UnifiedProfileDialog({
|
|
|
474
474
|
className="inline-flex items-center gap-2"
|
|
475
475
|
style={{ whiteSpace: "nowrap" }}
|
|
476
476
|
>
|
|
477
|
-
<User className="h-4 w-4 text-
|
|
477
|
+
<User className="h-4 w-4 text-emerald-600 shrink-0" />
|
|
478
478
|
Khách hàng
|
|
479
479
|
</span>
|
|
480
480
|
</SelectItem>
|
|
@@ -483,7 +483,7 @@ export function UnifiedProfileDialog({
|
|
|
483
483
|
className="inline-flex items-center gap-2"
|
|
484
484
|
style={{ whiteSpace: "nowrap" }}
|
|
485
485
|
>
|
|
486
|
-
<Store className="h-4 w-4 text-
|
|
486
|
+
<Store className="h-4 w-4 text-orange-600 shrink-0" />
|
|
487
487
|
Nhà cung cấp
|
|
488
488
|
</span>
|
|
489
489
|
</SelectItem>
|
|
@@ -492,17 +492,17 @@ export function UnifiedProfileDialog({
|
|
|
492
492
|
) : (
|
|
493
493
|
<div className="flex items-center gap-1.5 shrink-0">
|
|
494
494
|
{userType === "employee" && (
|
|
495
|
-
<span className="text-[12px] px-2.5 py-1 font-semibold rounded-md bg-
|
|
495
|
+
<span className="text-[12px] px-2.5 py-1 font-semibold rounded-md bg-primary/10 text-primary border border-primary/20 inline-flex items-center">
|
|
496
496
|
<UserCog className="h-3.5 w-3.5 mr-1.5" /> Nhân viên
|
|
497
497
|
</span>
|
|
498
498
|
)}
|
|
499
499
|
{userType === "customer" && (
|
|
500
|
-
<span className="text-[12px] px-2.5 py-1 font-semibold rounded-md bg-
|
|
500
|
+
<span className="text-[12px] px-2.5 py-1 font-semibold rounded-md bg-emerald-600 text-white inline-flex items-center">
|
|
501
501
|
<User className="h-3.5 w-3.5 mr-1.5" /> Khách hàng
|
|
502
502
|
</span>
|
|
503
503
|
)}
|
|
504
504
|
{userType === "supplier" && (
|
|
505
|
-
<span className="text-[12px] px-2.5 py-1 font-semibold rounded-md bg-
|
|
505
|
+
<span className="text-[12px] px-2.5 py-1 font-semibold rounded-md bg-orange-600 text-white inline-flex items-center">
|
|
506
506
|
<Store className="h-3.5 w-3.5 mr-1.5" /> Nhà cung cấp
|
|
507
507
|
</span>
|
|
508
508
|
)}
|
|
@@ -512,7 +512,7 @@ export function UnifiedProfileDialog({
|
|
|
512
512
|
<Button
|
|
513
513
|
variant="outline"
|
|
514
514
|
onClick={() => onOpenChange(false)}
|
|
515
|
-
className="border border-
|
|
515
|
+
className="border border-border bg-background text-foreground hover:bg-accent gap-2 h-9 rounded-md px-4 text-[13px] font-medium"
|
|
516
516
|
>
|
|
517
517
|
<span className="hidden sm:inline">Đóng</span>
|
|
518
518
|
<X className="h-4 w-4 sm:hidden" />
|
|
@@ -542,7 +542,7 @@ export function UnifiedProfileDialog({
|
|
|
542
542
|
"relative pb-3 text-[14px] font-medium transition-all whitespace-nowrap flex items-center gap-2",
|
|
543
543
|
activeSection === item.id
|
|
544
544
|
? "text-primary dark:text-primary"
|
|
545
|
-
: "text-
|
|
545
|
+
: "text-muted-foreground hover:text-foreground"
|
|
546
546
|
)}
|
|
547
547
|
>
|
|
548
548
|
<item.icon
|
|
@@ -550,7 +550,7 @@ export function UnifiedProfileDialog({
|
|
|
550
550
|
"h-[16px] w-[16px]",
|
|
551
551
|
activeSection === item.id
|
|
552
552
|
? "text-primary dark:text-primary"
|
|
553
|
-
: "text-
|
|
553
|
+
: "text-muted-foreground"
|
|
554
554
|
)}
|
|
555
555
|
strokeWidth={2}
|
|
556
556
|
/>
|
|
@@ -564,7 +564,7 @@ export function UnifiedProfileDialog({
|
|
|
564
564
|
</div>
|
|
565
565
|
|
|
566
566
|
{/* MAIN CONTENT AREA */}
|
|
567
|
-
<div className="flex-1 bg-
|
|
567
|
+
<div className="flex-1 bg-background overflow-y-auto overscroll-contain touch-pan-y" style={{ WebkitOverflowScrolling: 'touch' }}>
|
|
568
568
|
<div className="p-4 sm:p-5 max-w-4xl mx-auto space-y-4 pb-20">
|
|
569
569
|
{/* General Section */}
|
|
570
570
|
{activeSection === "general" && (
|
|
@@ -572,8 +572,8 @@ export function UnifiedProfileDialog({
|
|
|
572
572
|
|
|
573
573
|
<div className="grid md:grid-cols-2 gap-x-6 gap-y-4">
|
|
574
574
|
<div className="space-y-1.5">
|
|
575
|
-
<Label className="text-[13px] font-medium text-
|
|
576
|
-
Họ và tên <span className="text-
|
|
575
|
+
<Label className="text-[13px] font-medium text-foreground">
|
|
576
|
+
Họ và tên <span className="text-destructive">*</span>
|
|
577
577
|
</Label>
|
|
578
578
|
<Input
|
|
579
579
|
value={profileData.name}
|
|
@@ -584,11 +584,11 @@ export function UnifiedProfileDialog({
|
|
|
584
584
|
})
|
|
585
585
|
}
|
|
586
586
|
placeholder="Nguyễn Văn A"
|
|
587
|
-
className="bg-
|
|
587
|
+
className="bg-background border-input focus:ring-primary focus:border-primary rounded-md h-9 text-[13px]"
|
|
588
588
|
/>
|
|
589
589
|
</div>
|
|
590
590
|
<div className="space-y-1.5">
|
|
591
|
-
<Label className="text-[13px] font-medium text-
|
|
591
|
+
<Label className="text-[13px] font-medium text-foreground">Email</Label>
|
|
592
592
|
<Input
|
|
593
593
|
value={profileData.email}
|
|
594
594
|
onChange={(e) =>
|
|
@@ -598,11 +598,11 @@ export function UnifiedProfileDialog({
|
|
|
598
598
|
})
|
|
599
599
|
}
|
|
600
600
|
placeholder="email@example.com"
|
|
601
|
-
className="bg-
|
|
601
|
+
className="bg-background border-input focus:ring-primary focus:border-primary rounded-md h-9 text-[13px]"
|
|
602
602
|
/>
|
|
603
603
|
</div>
|
|
604
604
|
<div className="space-y-1.5">
|
|
605
|
-
<Label className="text-[13px] font-medium text-
|
|
605
|
+
<Label className="text-[13px] font-medium text-foreground">Số điện thoại</Label>
|
|
606
606
|
<Input
|
|
607
607
|
value={profileData.phone}
|
|
608
608
|
onChange={(e) =>
|
|
@@ -612,11 +612,11 @@ export function UnifiedProfileDialog({
|
|
|
612
612
|
})
|
|
613
613
|
}
|
|
614
614
|
placeholder="0912..."
|
|
615
|
-
className="bg-
|
|
615
|
+
className="bg-background border-input focus:ring-primary focus:border-primary rounded-md h-9 text-[13px]"
|
|
616
616
|
/>
|
|
617
617
|
</div>
|
|
618
618
|
<div className="space-y-1.5">
|
|
619
|
-
<Label className="text-[13px] font-medium text-
|
|
619
|
+
<Label className="text-[13px] font-medium text-foreground">Địa chỉ</Label>
|
|
620
620
|
<Input
|
|
621
621
|
value={profileData.address}
|
|
622
622
|
onChange={(e) =>
|
|
@@ -626,14 +626,14 @@ export function UnifiedProfileDialog({
|
|
|
626
626
|
})
|
|
627
627
|
}
|
|
628
628
|
placeholder="Số nhà, đường..."
|
|
629
|
-
className="bg-
|
|
629
|
+
className="bg-background border-input focus:ring-primary focus:border-primary rounded-md h-9 text-[13px]"
|
|
630
630
|
/>
|
|
631
631
|
</div>
|
|
632
632
|
{/* Status Toggle */}
|
|
633
633
|
{viewMode === "admin" && (
|
|
634
634
|
<div className="space-y-1.5">
|
|
635
|
-
<Label className="text-[13px] font-medium text-
|
|
636
|
-
<div className="flex items-center gap-3 h-9 px-3 rounded-[6px] border border-
|
|
635
|
+
<Label className="text-[13px] font-medium text-foreground">Trạng thái</Label>
|
|
636
|
+
<div className="flex items-center gap-3 h-9 px-3 rounded-[6px] border border-input bg-background">
|
|
637
637
|
<Switch
|
|
638
638
|
id="user-active-switch"
|
|
639
639
|
checked={isActive}
|
|
@@ -647,7 +647,7 @@ export function UnifiedProfileDialog({
|
|
|
647
647
|
<span
|
|
648
648
|
className={cn(
|
|
649
649
|
"text-[13px] font-bold tracking-[-0.13px]",
|
|
650
|
-
isActive ? "text-
|
|
650
|
+
isActive ? "text-success-text" : "text-muted-foreground",
|
|
651
651
|
)}
|
|
652
652
|
>
|
|
653
653
|
{isActive ? "Hoạt động" : "Đã khóa"}
|
|
@@ -657,7 +657,7 @@ export function UnifiedProfileDialog({
|
|
|
657
657
|
)}
|
|
658
658
|
{/* Additional Info for All */}
|
|
659
659
|
<div className="space-y-1.5">
|
|
660
|
-
<Label className="text-[13px] font-medium text-
|
|
660
|
+
<Label className="text-[13px] font-medium text-foreground">Ngày sinh</Label>
|
|
661
661
|
<DatePicker
|
|
662
662
|
value={ymdToDate(profileData.birthday)}
|
|
663
663
|
onValueChange={(d) =>
|
|
@@ -668,18 +668,18 @@ export function UnifiedProfileDialog({
|
|
|
668
668
|
}
|
|
669
669
|
formatStr="dd/MM/yyyy"
|
|
670
670
|
placeholder="dd/mm/yyyy"
|
|
671
|
-
buttonClassName="bg-
|
|
671
|
+
buttonClassName="bg-background border-input focus:ring-primary focus:border-primary rounded-md h-9 text-[13px]"
|
|
672
672
|
/>
|
|
673
673
|
</div>
|
|
674
674
|
<div className="space-y-1.5">
|
|
675
|
-
<Label className="text-[13px] font-medium text-
|
|
675
|
+
<Label className="text-[13px] font-medium text-foreground">Giới tính</Label>
|
|
676
676
|
<Select
|
|
677
677
|
value={profileData.gender}
|
|
678
678
|
onValueChange={(val) =>
|
|
679
679
|
setProfileData({ ...profileData, gender: val })
|
|
680
680
|
}
|
|
681
681
|
>
|
|
682
|
-
<SelectTrigger className="bg-
|
|
682
|
+
<SelectTrigger className="bg-background border-input focus:ring-primary focus:border-primary rounded-md h-9 text-[13px]">
|
|
683
683
|
<SelectValue placeholder="Chọn giới tính" />
|
|
684
684
|
</SelectTrigger>
|
|
685
685
|
<SelectContent>
|
|
@@ -690,7 +690,7 @@ export function UnifiedProfileDialog({
|
|
|
690
690
|
</Select>
|
|
691
691
|
</div>
|
|
692
692
|
<div className="space-y-1.5">
|
|
693
|
-
<Label className="text-[13px] font-medium text-
|
|
693
|
+
<Label className="text-[13px] font-medium text-foreground">CCCD/CMND</Label>
|
|
694
694
|
<Input
|
|
695
695
|
value={profileData.idNumber}
|
|
696
696
|
onChange={(e) =>
|
|
@@ -699,14 +699,14 @@ export function UnifiedProfileDialog({
|
|
|
699
699
|
idNumber: e.target.value,
|
|
700
700
|
})
|
|
701
701
|
}
|
|
702
|
-
className="bg-
|
|
702
|
+
className="bg-background border-input focus:ring-primary focus:border-primary rounded-md h-9 text-[13px]"
|
|
703
703
|
/>
|
|
704
704
|
</div>
|
|
705
705
|
|
|
706
706
|
{/* Customer Specific */}
|
|
707
707
|
{isCustomer && (
|
|
708
708
|
<div className="space-y-1.5">
|
|
709
|
-
<Label className="text-[13px] font-medium text-
|
|
709
|
+
<Label className="text-[13px] font-medium text-foreground">
|
|
710
710
|
Nhóm khách hàng
|
|
711
711
|
</Label>
|
|
712
712
|
<Select
|
|
@@ -715,7 +715,7 @@ export function UnifiedProfileDialog({
|
|
|
715
715
|
setProfileData({ ...profileData, groupId: val })
|
|
716
716
|
}
|
|
717
717
|
>
|
|
718
|
-
<SelectTrigger className="bg-
|
|
718
|
+
<SelectTrigger className="bg-background border-input focus:ring-primary focus:border-primary rounded-md h-9 text-[13px]">
|
|
719
719
|
<SelectValue placeholder="Chọn nhóm" />
|
|
720
720
|
</SelectTrigger>
|
|
721
721
|
<SelectContent>
|
|
@@ -732,8 +732,8 @@ export function UnifiedProfileDialog({
|
|
|
732
732
|
|
|
733
733
|
{isCustomer && (
|
|
734
734
|
<div className="pt-4">
|
|
735
|
-
<div className="flex items-center justify-between mb-4 border-b border-
|
|
736
|
-
<h3 className="text-[18px] font-bold tracking-[-0.14px] text-
|
|
735
|
+
<div className="flex items-center justify-between mb-4 border-b border-border pb-2">
|
|
736
|
+
<h3 className="text-[18px] font-bold tracking-[-0.14px] text-foreground">
|
|
737
737
|
Tài khoản đăng nhập
|
|
738
738
|
</h3>
|
|
739
739
|
<Switch
|
|
@@ -746,7 +746,7 @@ export function UnifiedProfileDialog({
|
|
|
746
746
|
<div className="space-y-4 animate-in fade-in slide-in-from-top-2">
|
|
747
747
|
<div className="grid md:grid-cols-2 gap-x-6 gap-y-4">
|
|
748
748
|
<div className="space-y-1.5 col-span-2 md:col-span-1">
|
|
749
|
-
<Label className="text-[13px] font-medium text-
|
|
749
|
+
<Label className="text-[13px] font-medium text-foreground">
|
|
750
750
|
Mật khẩu
|
|
751
751
|
</Label>
|
|
752
752
|
<div className="relative">
|
|
@@ -760,13 +760,13 @@ export function UnifiedProfileDialog({
|
|
|
760
760
|
}))
|
|
761
761
|
}
|
|
762
762
|
placeholder="Nhập mật khẩu"
|
|
763
|
-
className="pr-10 bg-
|
|
763
|
+
className="pr-10 bg-background border-input focus:ring-primary focus:border-primary rounded-[6px] h-9 text-[14px]"
|
|
764
764
|
/>
|
|
765
765
|
<Button
|
|
766
766
|
type="button"
|
|
767
767
|
variant="ghost"
|
|
768
768
|
size="icon"
|
|
769
|
-
className="absolute right-0 top-0 h-full w-9 text-
|
|
769
|
+
className="absolute right-0 top-0 h-full w-9 text-muted-foreground hover:text-foreground"
|
|
770
770
|
onClick={() =>
|
|
771
771
|
setShowPassword(!showPassword)
|
|
772
772
|
}
|
|
@@ -778,10 +778,10 @@ export function UnifiedProfileDialog({
|
|
|
778
778
|
)}
|
|
779
779
|
</Button>
|
|
780
780
|
</div>
|
|
781
|
-
<p className="text-[12px] text-
|
|
782
|
-
<span className="inline-block w-1.5 h-1.5 rounded-full bg-
|
|
781
|
+
<p className="text-[12px] text-muted-foreground mt-1.5 flex items-center gap-1.5">
|
|
782
|
+
<span className="inline-block w-1.5 h-1.5 rounded-full bg-primary" />
|
|
783
783
|
Mặc định:{" "}
|
|
784
|
-
<span className="font-mono font-bold text-
|
|
784
|
+
<span className="font-mono font-bold text-foreground">
|
|
785
785
|
Goeat@123456
|
|
786
786
|
</span>
|
|
787
787
|
</p>
|
|
@@ -789,7 +789,7 @@ export function UnifiedProfileDialog({
|
|
|
789
789
|
</div>
|
|
790
790
|
|
|
791
791
|
<div className="space-y-2 pt-2">
|
|
792
|
-
<Label className="text-[13px] font-medium text-
|
|
792
|
+
<Label className="text-[13px] font-medium text-foreground">
|
|
793
793
|
Phân quyền
|
|
794
794
|
</Label>
|
|
795
795
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
|
|
@@ -801,10 +801,10 @@ export function UnifiedProfileDialog({
|
|
|
801
801
|
<div
|
|
802
802
|
key={role.code}
|
|
803
803
|
className={cn(
|
|
804
|
-
"flex items-start gap-3 p-3 rounded-md border cursor-pointer hover:bg-
|
|
804
|
+
"flex items-start gap-3 p-3 rounded-md border cursor-pointer hover:bg-accent/50 transition-colors",
|
|
805
805
|
isSelected
|
|
806
|
-
? "border-
|
|
807
|
-
: "bg-background border-
|
|
806
|
+
? "border-primary bg-primary/5"
|
|
807
|
+
: "bg-background border-border",
|
|
808
808
|
)}
|
|
809
809
|
onClick={() => {
|
|
810
810
|
setSelectedRoles((prev) =>
|
|
@@ -818,10 +818,10 @@ export function UnifiedProfileDialog({
|
|
|
818
818
|
>
|
|
819
819
|
<Checkbox
|
|
820
820
|
checked={isSelected}
|
|
821
|
-
className="mt-1 data-[state=checked]:bg-
|
|
821
|
+
className="mt-1 data-[state=checked]:bg-primary data-[state=checked]:border-primary"
|
|
822
822
|
/>
|
|
823
823
|
<div className="space-y-0.5">
|
|
824
|
-
<p className="text-sm font-medium text-
|
|
824
|
+
<p className="text-sm font-medium text-foreground">
|
|
825
825
|
{role.name}
|
|
826
826
|
</p>
|
|
827
827
|
<p className="text-xs text-muted-foreground line-clamp-1">
|
|
@@ -845,7 +845,7 @@ export function UnifiedProfileDialog({
|
|
|
845
845
|
<div className="space-y-4 animate-in fade-in zoom-in-95 duration-200">
|
|
846
846
|
<div className="grid md:grid-cols-2 gap-x-8 gap-y-6">
|
|
847
847
|
<div className="space-y-2">
|
|
848
|
-
<Label className="text-[13px] font-medium text-
|
|
848
|
+
<Label className="text-[13px] font-medium text-foreground">Phòng ban</Label>
|
|
849
849
|
<Select
|
|
850
850
|
value={profileData.departmentId}
|
|
851
851
|
onValueChange={(val) =>
|
|
@@ -855,7 +855,7 @@ export function UnifiedProfileDialog({
|
|
|
855
855
|
})
|
|
856
856
|
}
|
|
857
857
|
>
|
|
858
|
-
<SelectTrigger className="bg-
|
|
858
|
+
<SelectTrigger className="bg-background border-input focus:ring-primary focus:border-primary shadow-sm rounded-[6px]">
|
|
859
859
|
<SelectValue placeholder="Chọn phòng ban" />
|
|
860
860
|
</SelectTrigger>
|
|
861
861
|
<SelectContent>
|
|
@@ -868,14 +868,14 @@ export function UnifiedProfileDialog({
|
|
|
868
868
|
</Select>
|
|
869
869
|
</div>
|
|
870
870
|
<div className="space-y-2">
|
|
871
|
-
<Label className="text-[13px] font-medium text-
|
|
871
|
+
<Label className="text-[13px] font-medium text-foreground">Chức danh</Label>
|
|
872
872
|
<Select
|
|
873
873
|
value={profileData.jobTitleId}
|
|
874
874
|
onValueChange={(val) =>
|
|
875
875
|
setProfileData({ ...profileData, jobTitleId: val })
|
|
876
876
|
}
|
|
877
877
|
>
|
|
878
|
-
<SelectTrigger className="bg-
|
|
878
|
+
<SelectTrigger className="bg-background border-input focus:ring-primary focus:border-primary shadow-sm rounded-[6px]">
|
|
879
879
|
<SelectValue placeholder="Chọn chức danh" />
|
|
880
880
|
</SelectTrigger>
|
|
881
881
|
<SelectContent>
|
|
@@ -889,8 +889,8 @@ export function UnifiedProfileDialog({
|
|
|
889
889
|
</div>
|
|
890
890
|
<div className="col-span-2 space-y-3 pt-2">
|
|
891
891
|
<div className="flex items-center justify-between gap-3 flex-wrap">
|
|
892
|
-
<Label className="text-[13px] font-medium text-
|
|
893
|
-
<span className="inline-flex items-center gap-1 text-[12px] text-
|
|
892
|
+
<Label className="text-[13px] font-medium text-foreground">Chi nhánh làm việc</Label>
|
|
893
|
+
<span className="inline-flex items-center gap-1 text-[12px] text-muted-foreground">
|
|
894
894
|
<Star className="h-3 w-3" />
|
|
895
895
|
Đánh dấu một chi nhánh mặc định
|
|
896
896
|
</span>
|
|
@@ -904,23 +904,23 @@ export function UnifiedProfileDialog({
|
|
|
904
904
|
<div
|
|
905
905
|
key={branch.id}
|
|
906
906
|
className={cn(
|
|
907
|
-
"flex items-center gap-3 p-3 rounded-md border cursor-pointer hover:bg-
|
|
907
|
+
"flex items-center gap-3 p-3 rounded-md border cursor-pointer hover:bg-accent/50 transition-colors",
|
|
908
908
|
isSelected
|
|
909
|
-
? "border-
|
|
910
|
-
: "bg-background border-
|
|
909
|
+
? "border-primary bg-primary/5"
|
|
910
|
+
: "bg-background border-border",
|
|
911
911
|
)}
|
|
912
912
|
onClick={() => toggleBranch(branch.id)}
|
|
913
913
|
>
|
|
914
914
|
<Checkbox
|
|
915
915
|
checked={isSelected}
|
|
916
|
-
className="data-[state=checked]:bg-
|
|
916
|
+
className="data-[state=checked]:bg-primary data-[state=checked]:border-primary"
|
|
917
917
|
/>
|
|
918
|
-
<span className="flex-1 text-sm font-medium text-
|
|
918
|
+
<span className="flex-1 text-sm font-medium text-foreground">
|
|
919
919
|
{branch.name}
|
|
920
920
|
</span>
|
|
921
921
|
{isSelected &&
|
|
922
922
|
(isDefault ? (
|
|
923
|
-
<span className="inline-flex items-center gap-1 rounded-full bg-
|
|
923
|
+
<span className="inline-flex items-center gap-1 rounded-full bg-primary px-2 py-0.5 text-[11px] font-semibold text-primary-foreground shrink-0">
|
|
924
924
|
<Star className="h-3 w-3 fill-current" />
|
|
925
925
|
Mặc định
|
|
926
926
|
</span>
|
|
@@ -932,7 +932,7 @@ export function UnifiedProfileDialog({
|
|
|
932
932
|
markDefaultBranch(branch.id);
|
|
933
933
|
}}
|
|
934
934
|
title="Đặt làm chi nhánh mặc định"
|
|
935
|
-
className="inline-flex items-center gap-1 rounded-full border border-
|
|
935
|
+
className="inline-flex items-center gap-1 rounded-full border border-border px-2 py-0.5 text-[11px] font-medium text-muted-foreground hover:border-primary/50 hover:text-primary transition-colors shrink-0"
|
|
936
936
|
>
|
|
937
937
|
<Star className="h-3 w-3" />
|
|
938
938
|
Đặt mặc định
|
|
@@ -951,7 +951,7 @@ export function UnifiedProfileDialog({
|
|
|
951
951
|
<div className="space-y-4 animate-in fade-in zoom-in-95 duration-200">
|
|
952
952
|
<div className="max-w-2xl space-y-6">
|
|
953
953
|
<div className="space-y-2">
|
|
954
|
-
<Label className="text-[13px] font-medium text-
|
|
954
|
+
<Label className="text-[13px] font-medium text-foreground">
|
|
955
955
|
Chọn nhà cung cấp
|
|
956
956
|
</Label>
|
|
957
957
|
<Select
|
|
@@ -960,7 +960,7 @@ export function UnifiedProfileDialog({
|
|
|
960
960
|
setProfileData({ ...profileData, supplierId: val })
|
|
961
961
|
}
|
|
962
962
|
>
|
|
963
|
-
<SelectTrigger className="bg-
|
|
963
|
+
<SelectTrigger className="bg-background border-input focus:ring-primary focus:border-primary shadow-sm rounded-[6px]">
|
|
964
964
|
<SelectValue placeholder="Chọn nhà cung cấp" />
|
|
965
965
|
</SelectTrigger>
|
|
966
966
|
<SelectContent>
|
|
@@ -972,7 +972,7 @@ export function UnifiedProfileDialog({
|
|
|
972
972
|
</SelectContent>
|
|
973
973
|
</Select>
|
|
974
974
|
</div>
|
|
975
|
-
<div className="rounded-md bg-
|
|
975
|
+
<div className="rounded-md bg-info-subtle p-4 text-sm text-info-text flex items-start gap-3">
|
|
976
976
|
<Store className="h-5 w-5 mt-0.5 shrink-0" />
|
|
977
977
|
<p>
|
|
978
978
|
Tài khoản này sẽ được liên kết với nhà cung cấp đã
|
|
@@ -986,14 +986,14 @@ export function UnifiedProfileDialog({
|
|
|
986
986
|
|
|
987
987
|
{activeSection === "roles" && (
|
|
988
988
|
<div className="animate-in fade-in zoom-in-95 duration-200">
|
|
989
|
-
<div className="sticky -top-4 sm:-top-5 pt-2 sm:pt-2 pb-4 -mx-4 sm:-mx-5 px-4 sm:px-5 bg-
|
|
989
|
+
<div className="sticky -top-4 sm:-top-5 pt-2 sm:pt-2 pb-4 -mx-4 sm:-mx-5 px-4 sm:px-5 bg-background/95 backdrop-blur-sm z-10 border-b border-border mb-4 shadow-sm">
|
|
990
990
|
<div className="relative max-w-md">
|
|
991
|
-
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-
|
|
991
|
+
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
|
|
992
992
|
<Input
|
|
993
993
|
placeholder="Tìm kiếm vai trò..."
|
|
994
994
|
value={roleSearchQuery}
|
|
995
995
|
onChange={(e) => setRoleSearchQuery(e.target.value)}
|
|
996
|
-
className="pl-9 bg-
|
|
996
|
+
className="pl-9 bg-background border-input focus:ring-primary focus:border-primary shadow-sm rounded-md h-9 text-[13px]"
|
|
997
997
|
/>
|
|
998
998
|
</div>
|
|
999
999
|
</div>
|
|
@@ -1005,10 +1005,10 @@ export function UnifiedProfileDialog({
|
|
|
1005
1005
|
<div
|
|
1006
1006
|
key={role.code}
|
|
1007
1007
|
className={cn(
|
|
1008
|
-
"flex items-start gap-3 p-3 rounded-md border cursor-pointer hover:bg-
|
|
1008
|
+
"flex items-start gap-3 p-3 rounded-md border cursor-pointer hover:bg-accent/50 transition-colors",
|
|
1009
1009
|
isSelected
|
|
1010
|
-
? "border-
|
|
1011
|
-
: "bg-background border-
|
|
1010
|
+
? "border-primary bg-primary/5"
|
|
1011
|
+
: "bg-background border-border",
|
|
1012
1012
|
)}
|
|
1013
1013
|
onClick={() => {
|
|
1014
1014
|
setSelectedRoles((prev) =>
|
|
@@ -1020,10 +1020,10 @@ export function UnifiedProfileDialog({
|
|
|
1020
1020
|
>
|
|
1021
1021
|
<Checkbox
|
|
1022
1022
|
checked={isSelected}
|
|
1023
|
-
className="mt-1 data-[state=checked]:bg-
|
|
1023
|
+
className="mt-1 data-[state=checked]:bg-primary data-[state=checked]:border-primary"
|
|
1024
1024
|
/>
|
|
1025
1025
|
<div className="space-y-0.5">
|
|
1026
|
-
<p className="text-sm font-medium text-
|
|
1026
|
+
<p className="text-sm font-medium text-foreground">
|
|
1027
1027
|
{role.name}
|
|
1028
1028
|
</p>
|
|
1029
1029
|
<p className="text-xs text-muted-foreground line-clamp-1">
|
|
@@ -1049,7 +1049,7 @@ export function UnifiedProfileDialog({
|
|
|
1049
1049
|
</div>
|
|
1050
1050
|
<div className="grid md:grid-cols-2 gap-x-8 gap-y-6">
|
|
1051
1051
|
<div className="space-y-2">
|
|
1052
|
-
<Label className="text-[13px] font-medium text-
|
|
1052
|
+
<Label className="text-[13px] font-medium text-foreground">Mật khẩu mới</Label>
|
|
1053
1053
|
<div className="relative">
|
|
1054
1054
|
<Input
|
|
1055
1055
|
type={showPassword ? "text" : "password"}
|
|
@@ -1060,7 +1060,7 @@ export function UnifiedProfileDialog({
|
|
|
1060
1060
|
password: e.target.value,
|
|
1061
1061
|
}))
|
|
1062
1062
|
}
|
|
1063
|
-
className="pr-10 bg-
|
|
1063
|
+
className="pr-10 bg-background border-input focus:ring-primary focus:border-primary shadow-sm rounded-[6px]"
|
|
1064
1064
|
/>
|
|
1065
1065
|
<Button
|
|
1066
1066
|
type="button"
|
|
@@ -1078,7 +1078,7 @@ export function UnifiedProfileDialog({
|
|
|
1078
1078
|
</div>
|
|
1079
1079
|
</div>
|
|
1080
1080
|
<div className="space-y-2">
|
|
1081
|
-
<Label className="text-[13px] font-medium text-
|
|
1081
|
+
<Label className="text-[13px] font-medium text-foreground">
|
|
1082
1082
|
Xác nhận mật khẩu
|
|
1083
1083
|
</Label>
|
|
1084
1084
|
<Input
|
|
@@ -1090,7 +1090,7 @@ export function UnifiedProfileDialog({
|
|
|
1090
1090
|
confirm: e.target.value,
|
|
1091
1091
|
}))
|
|
1092
1092
|
}
|
|
1093
|
-
className="bg-
|
|
1093
|
+
className="bg-background border-input focus:ring-primary focus:border-primary shadow-sm rounded-[6px]"
|
|
1094
1094
|
/>
|
|
1095
1095
|
</div>
|
|
1096
1096
|
</div>
|