@goplusvn/core 0.1.22 → 0.1.24

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.
@@ -1,10 +1,12 @@
1
1
  "use client";
2
2
 
3
3
  import { useMemo, useCallback, useState } from "react";
4
- import { Loader2, UserCheck, UserX, KeyRound } from "lucide-react";
5
- import {
6
- Button,
4
+ import type { ReactNode } from "react";
5
+ import { Loader2, UserX, KeyRound } from "lucide-react";
6
+ import {
7
+ Button,
7
8
  Badge,
9
+ StatusIndicator,
8
10
  AlertDialog,
9
11
  AlertDialogAction,
10
12
  AlertDialogCancel,
@@ -59,6 +61,8 @@ export interface RoleListPageProps {
59
61
  description: string | null;
60
62
  isDefault?: boolean;
61
63
  }>;
64
+ /** Nút Import app-specific (vd ImportDialog) — render trong toolbar khi có quyền import. */
65
+ importSlot?: ReactNode;
62
66
  }
63
67
 
64
68
  export function RoleListPage({
@@ -68,6 +72,7 @@ export function RoleListPage({
68
72
  initialData,
69
73
  resources,
70
74
  actions,
75
+ importSlot,
71
76
  }: RoleListPageProps) {
72
77
  const canCreate = permissions.create;
73
78
  const canUpdate = permissions.update;
@@ -199,7 +204,9 @@ export function RoleListPage({
199
204
 
200
205
  if (error) {
201
206
  return (
202
- <div className="text-red-500">Không thể tải dữ liệu: {error.message}</div>
207
+ <div className="text-destructive">
208
+ Không thể tải dữ liệu: {error.message}
209
+ </div>
203
210
  );
204
211
  }
205
212
 
@@ -223,6 +230,7 @@ export function RoleListPage({
223
230
  canCreate={canCreate}
224
231
  canImport={canImport}
225
232
  canExport={canExport}
233
+ importSlot={importSlot}
226
234
  />
227
235
 
228
236
  {/* Roles Grid */}
@@ -342,15 +350,9 @@ export function RoleListPage({
342
350
  </div>
343
351
  <div className="flex items-center space-x-2">
344
352
  {user.isActive ? (
345
- <Badge variant="default" className="text-xs">
346
- <UserCheck className="h-3 w-3 mr-1" />
347
- Active
348
- </Badge>
353
+ <StatusIndicator status="active" />
349
354
  ) : (
350
- <Badge variant="secondary" className="text-xs">
351
- <UserX className="h-3 w-3 mr-1" />
352
- Inactive
353
- </Badge>
355
+ <StatusIndicator status="inactive" />
354
356
  )}
355
357
  </div>
356
358
  </div>
@@ -681,7 +681,7 @@ export const MultiSelect = React.forwardRef<MultiSelectRef, MultiSelectProps>(
681
681
  {open && (
682
682
  <div
683
683
  ref={dropdownRef}
684
- className="absolute z-[100] mt-1 w-full rounded-md border bg-popover/95 backdrop-blur-md text-popover-foreground shadow-lg shadow-black/5 animate-in fade-in-0 zoom-in-95 slide-in-from-top-1"
684
+ className="absolute z-[100] mt-1 w-full min-w-[15rem] rounded-md border bg-popover/95 backdrop-blur-md text-popover-foreground shadow-lg shadow-black/5 animate-in fade-in-0 zoom-in-95 slide-in-from-top-1"
685
685
  style={{
686
686
  top: "100%",
687
687
  left: 0,
@@ -746,14 +746,15 @@ export const MultiSelect = React.forwardRef<MultiSelectRef, MultiSelectProps>(
746
746
  )}
747
747
  </div>
748
748
 
749
- {/* Action Buttons */}
750
- <div className="flex items-center justify-between border-b p-1">
751
- <div className="flex items-center">
749
+ {/* Action Buttons — hàng gọn, không cho tràn: nhóm trái min-w-0,
750
+ nhóm phải shrink-0; nút đóng màu trung tính (không phải xóa) */}
751
+ <div className="flex items-center justify-between gap-1 overflow-hidden border-b p-1">
752
+ <div className="flex min-w-0 items-center">
752
753
  <Button
753
754
  type="button"
754
755
  variant="ghost"
755
756
  size="sm"
756
- className="h-8 px-2 text-xs"
757
+ className="h-7 px-1.5 text-xs"
757
758
  onClick={(e) => {
758
759
  e.stopPropagation();
759
760
  handleSelectAll();
@@ -765,7 +766,7 @@ export const MultiSelect = React.forwardRef<MultiSelectRef, MultiSelectProps>(
765
766
  type="button"
766
767
  variant="ghost"
767
768
  size="sm"
768
- className="h-8 px-2 text-xs"
769
+ className="h-7 px-1.5 text-xs"
769
770
  onClick={(e) => {
770
771
  e.stopPropagation();
771
772
  handleClearAll();
@@ -774,7 +775,7 @@ export const MultiSelect = React.forwardRef<MultiSelectRef, MultiSelectProps>(
774
775
  Bỏ chọn
775
776
  </Button>
776
777
  </div>
777
- <div className="flex items-center">
778
+ <div className="flex shrink-0 items-center">
778
779
  <span
779
780
  className="whitespace-nowrap px-1 text-[11px] tabular-nums text-muted-foreground"
780
781
  title={`Đã chọn ${value.length} / ${
@@ -790,13 +791,13 @@ export const MultiSelect = React.forwardRef<MultiSelectRef, MultiSelectProps>(
790
791
  size="sm"
791
792
  aria-label="Đóng"
792
793
  title="Đóng"
793
- className="h-8 w-8 shrink-0 p-0 text-destructive hover:text-destructive hover:bg-destructive/10"
794
+ className="h-7 w-7 shrink-0 p-0 text-muted-foreground hover:text-foreground"
794
795
  onClick={(e) => {
795
796
  e.stopPropagation();
796
797
  setOpen(false);
797
798
  }}
798
799
  >
799
- <X className="h-4 w-4" />
800
+ <X className="h-3.5 w-3.5" />
800
801
  </Button>
801
802
  </div>
802
803
  </div>
@@ -35,7 +35,7 @@ export function HorizontalLayout({
35
35
  searchResults={searchResults}
36
36
  searchLoading={searchLoading}
37
37
  />
38
- <div className="w-full min-h-screen flex flex-col bg-muted/40">
38
+ <div className="w-full min-h-screen flex flex-col bg-background">
39
39
  <HorizontalLayoutHeader
40
40
  dictionary={dictionary}
41
41
  navigation={navigation}
@@ -396,7 +396,9 @@ const SidebarInset = React.forwardRef<
396
396
  <main
397
397
  ref={ref}
398
398
  className={cn(
399
- "relative flex min-h-svh flex-1 flex-col bg-muted/40 transition-[margin] duration-200 ease-linear",
399
+ // Canvas app dùng token bg-background (app tự quyết tông — vinhhoa/wu
400
+ // muốn nền TRẮNG); trước đây bg-muted/40 phủ xám toàn bộ trang.
401
+ "relative flex min-h-svh flex-1 flex-col bg-background transition-[margin] duration-200 ease-linear",
400
402
  // Inset: content is a card floating with a uniform m-2 gap on ALL sides
401
403
  // (no ml-0 override) so there's a small gap between the sidebar and the
402
404
  // content/header instead of them touching.