@goplusvn/core 0.1.0 → 0.1.1
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/assets/erp_wallpaper.png +0 -0
- package/src/assets/goeat_logo.png +0 -0
- package/src/audit/audit-manager.ts +139 -0
- package/src/audit/index.ts +11 -0
- package/src/audit/memory-audit-logger.ts +86 -0
- package/src/audit/types.ts +50 -0
- package/src/auth/auth-service.ts +97 -0
- package/src/auth/index.ts +266 -0
- package/src/code-generation/index.ts +69 -0
- package/src/configs/auth-routes.ts +17 -0
- package/src/configs/crud.ts +136 -0
- package/src/configs/data/navigations.ts +781 -0
- package/src/configs/data/oauth-links.ts +10 -0
- package/src/configs/entities/material-categories.config.ts +125 -0
- package/src/configs/i18n.ts +12 -0
- package/src/configs/index.ts +26 -0
- package/src/configs/status.ts +25 -0
- package/src/configs/themes.ts +100 -0
- package/src/crud/components/crud-bulk-actions.tsx +91 -0
- package/src/crud/components/crud-card-view.tsx +241 -0
- package/src/crud/components/crud-context.tsx +122 -0
- package/src/crud/components/crud-delete-dialog.tsx +145 -0
- package/src/crud/components/crud-dialog.tsx +406 -0
- package/src/crud/components/crud-empty-state.tsx +104 -0
- package/src/crud/components/crud-export-button.tsx +170 -0
- package/src/crud/components/crud-field-renderer.tsx +653 -0
- package/src/crud/components/crud-filter-chips.tsx +102 -0
- package/src/crud/components/crud-filters/checkbox-filter.tsx +97 -0
- package/src/crud/components/crud-filters/datetime-filter.tsx +83 -0
- package/src/crud/components/crud-filters/filter-builder.tsx +66 -0
- package/src/crud/components/crud-filters/index.tsx +76 -0
- package/src/crud/components/crud-filters/radio-filter.tsx +86 -0
- package/src/crud/components/crud-filters/select-filter.tsx +141 -0
- package/src/crud/components/crud-filters/text-filter.tsx +86 -0
- package/src/crud/components/crud-form.tsx +642 -0
- package/src/crud/components/crud-import-dialog.tsx +440 -0
- package/src/crud/components/crud-infinite-scroll.tsx +116 -0
- package/src/crud/components/crud-page.tsx +1017 -0
- package/src/crud/components/crud-provider.tsx +277 -0
- package/src/crud/components/crud-row-actions.tsx +189 -0
- package/src/crud/components/crud-search.tsx +82 -0
- package/src/crud/components/crud-sheet.tsx +336 -0
- package/src/crud/components/crud-table-skeleton.tsx +26 -0
- package/src/crud/components/crud-table-toolbar.tsx +91 -0
- package/src/crud/components/crud-table.tsx +352 -0
- package/src/crud/components/crud-virtual-table.tsx +55 -0
- package/src/crud/components/index.tsx +20 -0
- package/src/crud/crud-filters/checkbox-filter.tsx +87 -0
- package/src/crud/crud-filters/datetime-filter.tsx +82 -0
- package/src/crud/crud-filters/filter-builder.tsx +64 -0
- package/src/crud/crud-filters/index.tsx +78 -0
- package/src/crud/crud-filters/radio-filter.tsx +79 -0
- package/src/crud/crud-filters/select-filter.tsx +148 -0
- package/src/crud/crud-filters/text-filter.tsx +81 -0
- package/src/crud/index.ts +43 -0
- package/src/crud/lib/crud-service.test.ts +334 -0
- package/src/crud/lib/crud-service.ts +358 -0
- package/src/crud/lib/crud-utils.test.ts +354 -0
- package/src/crud/lib/crud-utils.ts +299 -0
- package/src/crud/lib/crud-validator.ts +247 -0
- package/src/crud/lib/data-loader.ts +234 -0
- package/src/crud/lib/field-calculator.ts +241 -0
- package/src/crud/lib/field-formatter.ts +240 -0
- package/src/crud/lib/import-export-service.test.ts +290 -0
- package/src/crud/lib/import-export-service.ts +352 -0
- package/src/crud/lib/import-server-utils.ts +109 -0
- package/src/crud/lib/lazy-loader.ts +241 -0
- package/src/crud/lib/parse-filters.ts +85 -0
- package/src/crud/lib/permissions.ts +52 -0
- package/src/crud/lib/serialize-config.ts +60 -0
- package/src/crud/lib/stream-loader.ts +145 -0
- package/src/crud/lib/translate-config.ts +335 -0
- package/src/crud/lib/types.ts +11 -0
- package/src/crud/pages/entity-crud-page.tsx +144 -0
- package/src/crud/server.ts +8 -0
- package/src/home/constants.tsx +142 -0
- package/src/home/feature-showcase.tsx +171 -0
- package/src/home/home-page.tsx +191 -0
- package/src/home/hooks/index.ts +1 -0
- package/src/home/hooks/useWidgetPreferences.ts +167 -0
- package/src/home/index.ts +33 -0
- package/src/home/quick-access-dialog.tsx +271 -0
- package/src/home/quick-access-menu.tsx +267 -0
- package/src/home/types.ts +140 -0
- package/src/home/welcome-card.tsx +92 -0
- package/src/home/widget-container.tsx +258 -0
- package/src/home/widgets/base-widget.tsx +200 -0
- package/src/home/widgets/customers-widget.tsx +74 -0
- package/src/home/widgets/index.ts +6 -0
- package/src/home/widgets/orders-widget.tsx +87 -0
- package/src/home/widgets/revenue-widget.tsx +71 -0
- package/src/home/widgets/stock-widget.tsx +109 -0
- package/src/hooks/index.tsx +598 -0
- package/src/hooks/use-tenant.test.tsx +30 -0
- package/src/hooks/use-tenant.ts +5 -0
- package/src/index.ts +17 -0
- package/src/infrastructure/__tests__/architecture-verification.spec.ts +103 -0
- package/src/infrastructure/api-service.ts +317 -0
- package/src/infrastructure/cache/cache-manager.ts +107 -0
- package/src/infrastructure/cache/cache.ts +120 -0
- package/src/infrastructure/cache/index.ts +8 -0
- package/src/infrastructure/cache/types.ts +48 -0
- package/src/infrastructure/cron/cron-manager.ts +239 -0
- package/src/infrastructure/cron/index.ts +6 -0
- package/src/infrastructure/cron/types.ts +41 -0
- package/src/infrastructure/event-bus/event-bus.ts +145 -0
- package/src/infrastructure/event-bus/index.ts +2 -0
- package/src/infrastructure/event-bus/types.ts +22 -0
- package/src/infrastructure/index.ts +32 -0
- package/src/infrastructure/lock/decorators.ts +67 -0
- package/src/infrastructure/lock/index.ts +2 -0
- package/src/infrastructure/lock/lock-manager.ts +33 -0
- package/src/infrastructure/logger/index.ts +2 -0
- package/src/infrastructure/logger/logger.ts +96 -0
- package/src/infrastructure/logger/types.ts +25 -0
- package/src/layout/index.tsx +185 -0
- package/src/navigation/index.ts +91 -0
- package/src/notification/index.ts +14 -0
- package/src/notification/notification-service.ts +120 -0
- package/src/notification/storage/in-memory.ts +56 -0
- package/src/notification/storage/index.ts +1 -0
- package/src/notification/types.ts +51 -0
- package/src/organization/branch-service.ts +299 -0
- package/src/organization/branches.config.ts +154 -0
- package/src/organization/index.ts +5 -0
- package/src/plugin/apps-registry.ts +97 -0
- package/src/plugin/index.ts +5 -0
- package/src/plugin/types.ts +41 -0
- package/src/providers/index.tsx +109 -0
- package/src/providers/tenant-provider.tsx +45 -0
- package/src/rbac/components/roles/role-card.tsx +158 -0
- package/src/rbac/components/roles/role-stats-cards.tsx +29 -0
- package/src/rbac/components/roles/role-toolbar.tsx +123 -0
- package/src/rbac/hooks/use-role-operations.ts +159 -0
- package/src/rbac/hooks/use-roles-data.ts +59 -0
- package/src/rbac/index.ts +297 -0
- package/src/rbac/lib/permission-helpers.ts +63 -0
- package/src/rbac/pages/action-list-page.tsx +25 -0
- package/src/rbac/pages/resource-list-page.tsx +25 -0
- package/src/rbac/pages/role-list-page.tsx +378 -0
- package/src/rbac/permission-service.ts +140 -0
- package/src/rbac/permissions.ts +135 -0
- package/src/rbac/resource-service.ts +115 -0
- package/src/rbac/resource-validator.ts +119 -0
- package/src/rbac/role-service.ts +165 -0
- package/src/rbac/server.ts +16 -0
- package/src/rbac/types.ts +38 -0
- package/src/schemas/action.schema.ts +66 -0
- package/src/schemas/branch.schema.ts +52 -0
- package/src/schemas/coming-soon-schema.ts +9 -0
- package/src/schemas/company.schema.ts +44 -0
- package/src/schemas/forgot-passward-schema.ts +9 -0
- package/src/schemas/index.ts +30 -0
- package/src/schemas/material-category.schema.ts +43 -0
- package/src/schemas/material-pricing.schema.ts +74 -0
- package/src/schemas/material.schema.ts +76 -0
- package/src/schemas/materials.ts +52 -0
- package/src/schemas/new-passward-schema.ts +15 -0
- package/src/schemas/partner-company.schema.ts +149 -0
- package/src/schemas/register-schema.ts +36 -0
- package/src/schemas/resource.schema.ts +133 -0
- package/src/schemas/role.schema.ts +11 -0
- package/src/schemas/sign-in-schema.ts +24 -0
- package/src/schemas/supplier-pricing.schema.ts +15 -0
- package/src/schemas/supplier.schema.ts +120 -0
- package/src/schemas/system-category-group.schema.ts +67 -0
- package/src/schemas/system-category.schema.ts +77 -0
- package/src/schemas/system-config.schema.ts +118 -0
- package/src/schemas/uom.schema.ts +75 -0
- package/src/schemas/user-supplier.schema.ts +179 -0
- package/src/schemas/user.schema.ts +18 -0
- package/src/schemas/verify-email-schema.ts +9 -0
- package/src/schemas/warehouse.schema.ts +49 -0
- package/src/system/components/categories/category-list.tsx +529 -0
- package/src/system/components/categories/category-manager.tsx +89 -0
- package/src/system/components/categories/group-sidebar.tsx +308 -0
- package/src/system/components/settings/setting-dialogs.tsx +197 -0
- package/src/system/components/settings/setting-field.tsx +291 -0
- package/src/system/components/settings/setting-form-dialog.tsx +308 -0
- package/src/system/components/settings/settings-groups.ts +80 -0
- package/src/system/components/settings/settings-search.tsx +71 -0
- package/src/system/components/settings/settings-section.tsx +74 -0
- package/src/system/components/settings/settings-sidebar.tsx +81 -0
- package/src/system/constants.ts +3 -0
- package/src/system/index.ts +150 -0
- package/src/system/job-manager.ts +176 -0
- package/src/system/pages/components/categories/category-list.tsx +537 -0
- package/src/system/pages/components/categories/category-manager.tsx +90 -0
- package/src/system/pages/components/categories/group-sidebar.tsx +311 -0
- package/src/system/pages/components/settings/sales-rules-settings.tsx +222 -0
- package/src/system/pages/components/settings/setting-dialogs.tsx +197 -0
- package/src/system/pages/components/settings/setting-field.tsx +292 -0
- package/src/system/pages/components/settings/setting-form-dialog.tsx +308 -0
- package/src/system/pages/components/settings/settings-groups.ts +87 -0
- package/src/system/pages/components/settings/settings-page.tsx +372 -0
- package/src/system/pages/components/settings/settings-search.tsx +71 -0
- package/src/system/pages/components/settings/settings-section.tsx +74 -0
- package/src/system/pages/components/settings/settings-sidebar.tsx +81 -0
- package/src/system/pages/components/settings/system-settings.tsx +244 -0
- package/src/system/pages/system-category-page.tsx +15 -0
- package/src/system/pages/system-settings-page.tsx +380 -0
- package/src/system/schemas/system-category-group.schema.ts +46 -0
- package/src/system/schemas/system-category.schema.ts +56 -0
- package/src/system/services/settings-service.ts +127 -0
- package/src/system/services/system-category-service.ts +63 -0
- package/src/system/types.ts +45 -0
- package/src/types/index.ts +703 -0
- package/src/ui/auth/auth-layout.tsx +135 -0
- package/src/ui/auth/forgot-password-form.tsx +98 -0
- package/src/ui/auth/index.tsx +7 -0
- package/src/ui/auth/new-password-form.tsx +107 -0
- package/src/ui/auth/oauth-links.tsx +30 -0
- package/src/ui/auth/register-form.tsx +202 -0
- package/src/ui/auth/sign-in-form.tsx +238 -0
- package/src/ui/auth/verify-email-form.tsx +104 -0
- package/src/ui/crud/index.tsx +10 -0
- package/src/ui/data-display/accordion.tsx +65 -0
- package/src/ui/data-display/aspect-ratio.tsx +11 -0
- package/src/ui/data-display/avatar.tsx +163 -0
- package/src/ui/data-display/bento-grid.tsx +77 -0
- package/src/ui/data-display/carousel.tsx +249 -0
- package/src/ui/data-display/chart.tsx +363 -0
- package/src/ui/data-display/code-block-highlight.tsx +54 -0
- package/src/ui/data-display/collapsible.tsx +42 -0
- package/src/ui/data-display/compact-stat-bar.tsx +149 -0
- package/src/ui/data-display/data-table/data-table-context.tsx +255 -0
- package/src/ui/data-display/data-table/data-table-empty-state.tsx +133 -0
- package/src/ui/data-display/data-table/data-table-skeleton.tsx +145 -0
- package/src/ui/data-display/data-table/data-table-toolbar.tsx +353 -0
- package/src/ui/data-display/data-table/data-table.tsx +597 -0
- package/src/ui/data-display/data-table/index.ts +44 -0
- package/src/ui/data-display/data-table-column-header.tsx +75 -0
- package/src/ui/data-display/data-table-pagination.tsx +130 -0
- package/src/ui/data-display/data-table-view-options.tsx +59 -0
- package/src/ui/data-display/formatted-number-input.tsx +210 -0
- package/src/ui/data-display/highlight.tsx +20 -0
- package/src/ui/data-display/hover-card.tsx +48 -0
- package/src/ui/data-display/index.tsx +50 -0
- package/src/ui/data-display/iphone-15-pro.tsx +114 -0
- package/src/ui/data-display/kanban/index.ts +4 -0
- package/src/ui/data-display/kanban/kanban-board.tsx +192 -0
- package/src/ui/data-display/kanban/kanban-column.tsx +74 -0
- package/src/ui/data-display/kanban/kanban-item.tsx +50 -0
- package/src/ui/data-display/kanban/kanban-types.ts +21 -0
- package/src/ui/data-display/kpi-card.tsx +68 -0
- package/src/ui/data-display/media-grid.tsx +110 -0
- package/src/ui/data-display/safari.tsx +175 -0
- package/src/ui/data-display/show-more-text.tsx +55 -0
- package/src/ui/data-display/tabs.tsx +68 -0
- package/src/ui/data-display/timeline.tsx +256 -0
- package/src/ui/feedback/alert.tsx +60 -0
- package/src/ui/feedback/context-menu.tsx +245 -0
- package/src/ui/feedback/drawer.tsx +132 -0
- package/src/ui/feedback/error-dialog.tsx +273 -0
- package/src/ui/feedback/index.tsx +183 -0
- package/src/ui/feedback/progress.tsx +32 -0
- package/src/ui/feedback/sheet.tsx +148 -0
- package/src/ui/feedback/sonner.tsx +36 -0
- package/src/ui/forms/command.tsx +157 -0
- package/src/ui/forms/date-picker.tsx +73 -0
- package/src/ui/forms/date-range-picker.tsx +76 -0
- package/src/ui/forms/date-time-picker.tsx +109 -0
- package/src/ui/forms/editor/editor-menu-bar.tsx +394 -0
- package/src/ui/forms/editor/index.tsx +130 -0
- package/src/ui/forms/editor/multi-select-example.tsx +1234 -0
- package/src/ui/forms/emoji-picker.tsx +109 -0
- package/src/ui/forms/file-dropzone.tsx +169 -0
- package/src/ui/forms/file-thumbnail.tsx +29 -0
- package/src/ui/forms/index.tsx +201 -0
- package/src/ui/forms/input-file.tsx +99 -0
- package/src/ui/forms/input-group.tsx +46 -0
- package/src/ui/forms/input-otp.tsx +81 -0
- package/src/ui/forms/input-phone.tsx +172 -0
- package/src/ui/forms/input-spin.tsx +116 -0
- package/src/ui/forms/input-tags.tsx +219 -0
- package/src/ui/forms/input-time.tsx +42 -0
- package/src/ui/forms/multi-select.tsx +629 -0
- package/src/ui/forms/multiple-date-picker.tsx +74 -0
- package/src/ui/forms/radio-group.tsx +42 -0
- package/src/ui/forms/rating.tsx +158 -0
- package/src/ui/forms/time-picker.tsx +57 -0
- package/src/ui/index.tsx +17 -0
- package/src/ui/layout/animated-list.tsx +77 -0
- package/src/ui/layout/animated-sidebar.tsx +294 -0
- package/src/ui/layout/command-menu.tsx +355 -0
- package/src/ui/layout/customizer.tsx +324 -0
- package/src/ui/layout/footer.tsx +43 -0
- package/src/ui/layout/full-screen-toggle.tsx +52 -0
- package/src/ui/layout/header-breadcrumb.tsx +77 -0
- package/src/ui/layout/horizontal-layout-header.tsx +83 -0
- package/src/ui/layout/horizontal-layout.tsx +50 -0
- package/src/ui/layout/index.tsx +25 -0
- package/src/ui/layout/language-dropdown.tsx +103 -0
- package/src/ui/layout/logo.tsx +63 -0
- package/src/ui/layout/main-layout.tsx +57 -0
- package/src/ui/layout/mode-dropdown.tsx +58 -0
- package/src/ui/layout/notification-dropdown.tsx +127 -0
- package/src/ui/layout/page-tabs.tsx +306 -0
- package/src/ui/layout/route-cache.tsx +214 -0
- package/src/ui/layout/sidebar-group-icon-menu.tsx +195 -0
- package/src/ui/layout/sidebar.tsx +279 -0
- package/src/ui/layout/tab-content-cache.tsx +201 -0
- package/src/ui/layout/tab-navigation-provider.tsx +536 -0
- package/src/ui/layout/toggle-mobile-sidebar.tsx +33 -0
- package/src/ui/layout/top-bar-header-menubar.tsx +412 -0
- package/src/ui/layout/user-dropdown.tsx +188 -0
- package/src/ui/layout/vertical-layout-header.tsx +65 -0
- package/src/ui/layout/vertical-layout.tsx +47 -0
- package/src/ui/management/audit-log-page.tsx +209 -0
- package/src/ui/management/cache-management.tsx +349 -0
- package/src/ui/management/index.ts +3 -0
- package/src/ui/management/job-management.tsx +308 -0
- package/src/ui/pages/not-found.tsx +30 -0
- package/src/ui/primitives/badge.tsx +66 -0
- package/src/ui/primitives/breadcrumb.tsx +103 -0
- package/src/ui/primitives/button.tsx +129 -0
- package/src/ui/primitives/calendar.tsx +74 -0
- package/src/ui/primitives/card.tsx +86 -0
- package/src/ui/primitives/checkbox.tsx +31 -0
- package/src/ui/primitives/client.ts +30 -0
- package/src/ui/primitives/combobox.tsx +290 -0
- package/src/ui/primitives/dialog.tsx +121 -0
- package/src/ui/primitives/dropdown-menu.tsx +239 -0
- package/src/ui/primitives/dynamic-icon.tsx +24 -0
- package/src/ui/primitives/index.tsx +134 -0
- package/src/ui/primitives/input-number.tsx +131 -0
- package/src/ui/primitives/input.tsx +22 -0
- package/src/ui/primitives/keyboard.tsx +23 -0
- package/src/ui/primitives/label.tsx +24 -0
- package/src/ui/primitives/menubar.tsx +262 -0
- package/src/ui/primitives/navigation-menu.tsx +157 -0
- package/src/ui/primitives/pagination.tsx +118 -0
- package/src/ui/primitives/popover.tsx +56 -0
- package/src/ui/primitives/prefetch-link.tsx +60 -0
- package/src/ui/primitives/resizable.tsx +59 -0
- package/src/ui/primitives/scroll-area.tsx +63 -0
- package/src/ui/primitives/select.tsx +172 -0
- package/src/ui/primitives/separator.tsx +51 -0
- package/src/ui/primitives/sidebar.tsx +844 -0
- package/src/ui/primitives/slider.tsx +27 -0
- package/src/ui/primitives/status-badge.tsx +47 -0
- package/src/ui/primitives/sticky-layout.tsx +50 -0
- package/src/ui/primitives/switch.tsx +29 -0
- package/src/ui/primitives/table.tsx +116 -0
- package/src/ui/primitives/tabs.tsx +55 -0
- package/src/ui/primitives/toggle-group.tsx +70 -0
- package/src/ui/primitives/toggle.tsx +47 -0
- package/src/ui/primitives/tooltip.tsx +59 -0
- package/src/user/components/dangerous-zone.tsx +34 -0
- package/src/user/components/delete-account-form.tsx +40 -0
- package/src/user/components/index.ts +4 -0
- package/src/user/components/profile-info-form.tsx +390 -0
- package/src/user/components/profile-info.tsx +32 -0
- package/src/user/components/unified-profile-dialog.tsx +1019 -0
- package/src/user/components/user-stats.tsx +27 -0
- package/src/user/components/user-toolbar.tsx +137 -0
- package/src/user/components/users-card-view.tsx +253 -0
- package/src/user/index.ts +11 -0
- package/src/user/pages/user-list-page.tsx +234 -0
- package/src/user/pages/users-client-page.tsx +385 -0
- package/src/user/profile-page.tsx +19 -0
- package/src/user/schemas.ts +68 -0
- package/src/user/types.ts +34 -0
- package/src/user/user-service.ts +538 -0
- package/src/utils/index.ts +906 -0
- package/src/workflow/activity-timeline.tsx +412 -0
- package/src/workflow/approval-workflow.tsx +31 -0
- package/src/workflow/index.ts +2 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
// @goerp/core/ui/data-display
|
|
2
|
+
// DataTablePagination component for table pagination
|
|
3
|
+
|
|
4
|
+
"use client";
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
ChevronLeft,
|
|
8
|
+
ChevronRight,
|
|
9
|
+
ChevronsLeft,
|
|
10
|
+
ChevronsRight,
|
|
11
|
+
} from "lucide-react";
|
|
12
|
+
|
|
13
|
+
import type { Table } from "@tanstack/react-table";
|
|
14
|
+
|
|
15
|
+
import { Button, buttonVariants } from "../primitives";
|
|
16
|
+
import {
|
|
17
|
+
Select,
|
|
18
|
+
SelectContent,
|
|
19
|
+
SelectItem,
|
|
20
|
+
SelectTrigger,
|
|
21
|
+
SelectValue,
|
|
22
|
+
} from "../primitives/client";
|
|
23
|
+
|
|
24
|
+
interface DataTablePaginationProps<TData> {
|
|
25
|
+
table: Table<TData>;
|
|
26
|
+
totalItems?: number;
|
|
27
|
+
currentPage?: number;
|
|
28
|
+
pageSize?: number;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function DataTablePagination<TData>({
|
|
32
|
+
table,
|
|
33
|
+
totalItems,
|
|
34
|
+
currentPage,
|
|
35
|
+
pageSize,
|
|
36
|
+
}: DataTablePaginationProps<TData>) {
|
|
37
|
+
const startItem =
|
|
38
|
+
totalItems && currentPage && pageSize
|
|
39
|
+
? (currentPage - 1) * pageSize + 1
|
|
40
|
+
: table.getState().pagination.pageIndex *
|
|
41
|
+
table.getState().pagination.pageSize +
|
|
42
|
+
1;
|
|
43
|
+
const endItem =
|
|
44
|
+
totalItems && currentPage && pageSize
|
|
45
|
+
? Math.min(currentPage * pageSize, totalItems)
|
|
46
|
+
: Math.min(
|
|
47
|
+
(table.getState().pagination.pageIndex + 1) *
|
|
48
|
+
table.getState().pagination.pageSize,
|
|
49
|
+
table.getFilteredRowModel().rows.length,
|
|
50
|
+
);
|
|
51
|
+
const total = totalItems ?? table.getFilteredRowModel().rows.length;
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<div className="flex flex-col items-center justify-between gap-2 py-1 md:flex-row md:gap-3">
|
|
55
|
+
<div className="flex-1 text-sm text-muted-foreground">
|
|
56
|
+
{total === 0 ? (
|
|
57
|
+
<span>Không có dữ liệu</span>
|
|
58
|
+
) : (
|
|
59
|
+
<span>
|
|
60
|
+
Hiển thị {startItem} - {endItem} trong tổng số {total} mục
|
|
61
|
+
</span>
|
|
62
|
+
)}
|
|
63
|
+
</div>
|
|
64
|
+
<div className="flex items-center gap-2">
|
|
65
|
+
<div className="flex items-center gap-2">
|
|
66
|
+
<p className="text-sm font-medium">Số dòng mỗi trang</p>
|
|
67
|
+
<Select
|
|
68
|
+
value={`${table.getState().pagination.pageSize}`}
|
|
69
|
+
onValueChange={(value) => {
|
|
70
|
+
table.setPageSize(Number(value));
|
|
71
|
+
}}
|
|
72
|
+
>
|
|
73
|
+
<SelectTrigger className="h-8 w-[70px]">
|
|
74
|
+
<SelectValue placeholder={table.getState().pagination.pageSize} />
|
|
75
|
+
</SelectTrigger>
|
|
76
|
+
<SelectContent side="top">
|
|
77
|
+
{[10, 20, 30, 50, 100].map((pageSize) => (
|
|
78
|
+
<SelectItem key={pageSize} value={`${pageSize}`}>
|
|
79
|
+
{pageSize}
|
|
80
|
+
</SelectItem>
|
|
81
|
+
))}
|
|
82
|
+
</SelectContent>
|
|
83
|
+
</Select>
|
|
84
|
+
</div>
|
|
85
|
+
<div className="flex items-center gap-1">
|
|
86
|
+
<Button
|
|
87
|
+
variant="outline"
|
|
88
|
+
className="hidden h-8 w-8 p-0 lg:flex"
|
|
89
|
+
onClick={() => table.setPageIndex(0)}
|
|
90
|
+
disabled={!table.getCanPreviousPage()}
|
|
91
|
+
>
|
|
92
|
+
<span className="sr-only">Đi tới trang đầu</span>
|
|
93
|
+
<ChevronsLeft className="h-4 w-4" />
|
|
94
|
+
</Button>
|
|
95
|
+
<Button
|
|
96
|
+
variant="outline"
|
|
97
|
+
className="h-8 w-8 p-0"
|
|
98
|
+
onClick={() => table.previousPage()}
|
|
99
|
+
disabled={!table.getCanPreviousPage()}
|
|
100
|
+
>
|
|
101
|
+
<span className="sr-only">Trang trước</span>
|
|
102
|
+
<ChevronLeft className="h-4 w-4" />
|
|
103
|
+
</Button>
|
|
104
|
+
<div className="flex items-center justify-center text-sm font-medium">
|
|
105
|
+
Trang {table.getState().pagination.pageIndex + 1} /{" "}
|
|
106
|
+
{table.getPageCount()}
|
|
107
|
+
</div>
|
|
108
|
+
<Button
|
|
109
|
+
variant="outline"
|
|
110
|
+
className="h-8 w-8 p-0"
|
|
111
|
+
onClick={() => table.nextPage()}
|
|
112
|
+
disabled={!table.getCanNextPage()}
|
|
113
|
+
>
|
|
114
|
+
<span className="sr-only">Trang sau</span>
|
|
115
|
+
<ChevronRight className="h-4 w-4" />
|
|
116
|
+
</Button>
|
|
117
|
+
<Button
|
|
118
|
+
variant="outline"
|
|
119
|
+
className="hidden h-8 w-8 p-0 lg:flex"
|
|
120
|
+
onClick={() => table.setPageIndex(table.getPageCount() - 1)}
|
|
121
|
+
disabled={!table.getCanNextPage()}
|
|
122
|
+
>
|
|
123
|
+
<span className="sr-only">Đi tới trang cuối</span>
|
|
124
|
+
<ChevronsRight className="h-4 w-4" />
|
|
125
|
+
</Button>
|
|
126
|
+
</div>
|
|
127
|
+
</div>
|
|
128
|
+
</div>
|
|
129
|
+
);
|
|
130
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { DropdownMenuTrigger } from "@radix-ui/react-dropdown-menu";
|
|
4
|
+
import { Settings2 } from "lucide-react";
|
|
5
|
+
import type { Table } from "@tanstack/react-table";
|
|
6
|
+
|
|
7
|
+
import { Button } from "../primitives/button";
|
|
8
|
+
import {
|
|
9
|
+
DropdownMenu,
|
|
10
|
+
DropdownMenuCheckboxItem,
|
|
11
|
+
DropdownMenuContent,
|
|
12
|
+
DropdownMenuLabel,
|
|
13
|
+
DropdownMenuSeparator,
|
|
14
|
+
} from "../primitives/client"; // Importing from same package primitives
|
|
15
|
+
|
|
16
|
+
interface DataTableViewOptionsProps<TData> {
|
|
17
|
+
table: Table<TData>;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function DataTableViewOptions<TData>({
|
|
21
|
+
table,
|
|
22
|
+
}: DataTableViewOptionsProps<TData>) {
|
|
23
|
+
return (
|
|
24
|
+
<DropdownMenu>
|
|
25
|
+
<DropdownMenuTrigger asChild>
|
|
26
|
+
<Button
|
|
27
|
+
variant="outline"
|
|
28
|
+
size="sm"
|
|
29
|
+
className="ml-auto hidden h-8 lg:flex"
|
|
30
|
+
>
|
|
31
|
+
<Settings2 className="mr-2 h-4 w-4" />
|
|
32
|
+
View
|
|
33
|
+
</Button>
|
|
34
|
+
</DropdownMenuTrigger>
|
|
35
|
+
<DropdownMenuContent align="end" className="w-[150px]">
|
|
36
|
+
<DropdownMenuLabel>Toggle columns</DropdownMenuLabel>
|
|
37
|
+
<DropdownMenuSeparator />
|
|
38
|
+
{table
|
|
39
|
+
.getAllColumns()
|
|
40
|
+
.filter(
|
|
41
|
+
(column) =>
|
|
42
|
+
typeof column.accessorFn !== "undefined" && column.getCanHide(),
|
|
43
|
+
)
|
|
44
|
+
.map((column) => {
|
|
45
|
+
return (
|
|
46
|
+
<DropdownMenuCheckboxItem
|
|
47
|
+
key={column.id}
|
|
48
|
+
className="capitalize"
|
|
49
|
+
checked={column.getIsVisible()}
|
|
50
|
+
onCheckedChange={(value) => column.toggleVisibility(!!value)}
|
|
51
|
+
>
|
|
52
|
+
{column.id}
|
|
53
|
+
</DropdownMenuCheckboxItem>
|
|
54
|
+
);
|
|
55
|
+
})}
|
|
56
|
+
</DropdownMenuContent>
|
|
57
|
+
</DropdownMenu>
|
|
58
|
+
);
|
|
59
|
+
}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
// @goerp/core/ui/data-display
|
|
2
|
+
// FormattedNumberInput component for number input with formatting
|
|
3
|
+
|
|
4
|
+
"use client";
|
|
5
|
+
|
|
6
|
+
import React, { useState, useEffect } from "react";
|
|
7
|
+
import { Input } from "../primitives";
|
|
8
|
+
|
|
9
|
+
// =============================
|
|
10
|
+
// Helper Functions
|
|
11
|
+
// =============================
|
|
12
|
+
|
|
13
|
+
// Giới hạn tối đa 2 số thập phân khi format
|
|
14
|
+
const formatNumber = (
|
|
15
|
+
value: number | null,
|
|
16
|
+
locale: string = "vi-VN",
|
|
17
|
+
): string => {
|
|
18
|
+
if (value === null || isNaN(value)) return "";
|
|
19
|
+
return value.toLocaleString(locale, {
|
|
20
|
+
minimumFractionDigits: 0,
|
|
21
|
+
maximumFractionDigits: 2,
|
|
22
|
+
});
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
// Parse string -> number, có xét tới locale & số thập phân, giới hạn 2 digits
|
|
26
|
+
const parseNumberFromString = (
|
|
27
|
+
raw: string,
|
|
28
|
+
allowNegative: boolean,
|
|
29
|
+
locale: string = "vi-VN",
|
|
30
|
+
): number | null => {
|
|
31
|
+
if (!raw) return null;
|
|
32
|
+
|
|
33
|
+
// Chuẩn hoá khoảng trắng
|
|
34
|
+
let cleaned = raw.replace(/[\s]/g, "");
|
|
35
|
+
|
|
36
|
+
// Xác định ký tự phân tách theo locale
|
|
37
|
+
const isEn = locale.startsWith("en"); // en-US, en-GB...
|
|
38
|
+
const decimalSep = isEn ? "." : ","; // en: 1,234.56 | vi: 1.234,56
|
|
39
|
+
const thousandSep = isEn ? "," : ".";
|
|
40
|
+
|
|
41
|
+
// Giữ lại: số, dấu . , , và -
|
|
42
|
+
cleaned = cleaned.replace(/[^0-9.,-]/g, "");
|
|
43
|
+
|
|
44
|
+
// Xử lý dấu âm
|
|
45
|
+
let negative = false;
|
|
46
|
+
const minusIndex = cleaned.indexOf("-");
|
|
47
|
+
if (minusIndex !== -1) {
|
|
48
|
+
if (allowNegative && minusIndex === 0) {
|
|
49
|
+
negative = true;
|
|
50
|
+
}
|
|
51
|
+
// bỏ tất cả dấu '-'
|
|
52
|
+
cleaned = cleaned.replace(/-/g, "");
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (!cleaned) return null;
|
|
56
|
+
|
|
57
|
+
// Tách phần nguyên & thập phân theo decimalSep (dùng lần xuất hiện cuối cùng)
|
|
58
|
+
const lastDecimalIndex = cleaned.lastIndexOf(decimalSep);
|
|
59
|
+
let intPart = "";
|
|
60
|
+
let fracPart = "";
|
|
61
|
+
|
|
62
|
+
if (lastDecimalIndex >= 0) {
|
|
63
|
+
intPart = cleaned.slice(0, lastDecimalIndex);
|
|
64
|
+
fracPart = cleaned.slice(lastDecimalIndex + 1);
|
|
65
|
+
} else {
|
|
66
|
+
intPart = cleaned;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Xoá thousandSep trong phần nguyên và phần thập phân
|
|
70
|
+
const tsRegex = new RegExp(`\\${thousandSep}`, "g");
|
|
71
|
+
intPart = intPart.replace(tsRegex, "");
|
|
72
|
+
fracPart = fracPart.replace(tsRegex, "");
|
|
73
|
+
|
|
74
|
+
// Giới hạn tối đa 2 chữ số thập phân
|
|
75
|
+
if (fracPart.length > 2) {
|
|
76
|
+
fracPart = fracPart.slice(0, 2);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Nếu không có phần thập phân thì coi là số nguyên
|
|
80
|
+
let normalized = "";
|
|
81
|
+
if (fracPart.length > 0) {
|
|
82
|
+
normalized = `${intPart}.${fracPart}`;
|
|
83
|
+
} else {
|
|
84
|
+
normalized = intPart;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (!normalized) return null;
|
|
88
|
+
const num = Number((negative ? "-" : "") + normalized);
|
|
89
|
+
return isNaN(num) ? null : num;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
// =============================
|
|
93
|
+
// FormattedNumberInput Component
|
|
94
|
+
// =============================
|
|
95
|
+
|
|
96
|
+
export interface FormattedNumberInputProps
|
|
97
|
+
extends Omit<
|
|
98
|
+
React.InputHTMLAttributes<HTMLInputElement>,
|
|
99
|
+
"value" | "onChange" | "type"
|
|
100
|
+
> {
|
|
101
|
+
value?: number | null;
|
|
102
|
+
onValueChange?: (value: number | null) => void;
|
|
103
|
+
min?: number;
|
|
104
|
+
max?: number;
|
|
105
|
+
step?: number;
|
|
106
|
+
placeholder?: string;
|
|
107
|
+
suffix?: string; // "VNĐ", "%", ...
|
|
108
|
+
allowNegative?: boolean;
|
|
109
|
+
locale?: string; // "vi-VN", "en-US", ...
|
|
110
|
+
className?: string;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export const FormattedNumberInput: React.FC<FormattedNumberInputProps> = ({
|
|
114
|
+
value,
|
|
115
|
+
onValueChange,
|
|
116
|
+
min,
|
|
117
|
+
max,
|
|
118
|
+
step = 1,
|
|
119
|
+
placeholder,
|
|
120
|
+
suffix,
|
|
121
|
+
allowNegative = false,
|
|
122
|
+
locale = "vi-VN",
|
|
123
|
+
className,
|
|
124
|
+
...inputProps
|
|
125
|
+
}) => {
|
|
126
|
+
const [internalNumber, setInternalNumber] = useState<number | null>(
|
|
127
|
+
value ?? null,
|
|
128
|
+
);
|
|
129
|
+
const [displayValue, setDisplayValue] = useState<string>(
|
|
130
|
+
formatNumber(value ?? null, locale),
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
useEffect(() => {
|
|
134
|
+
if (value !== internalNumber) {
|
|
135
|
+
setInternalNumber(value ?? null);
|
|
136
|
+
setDisplayValue(formatNumber(value ?? null, locale));
|
|
137
|
+
}
|
|
138
|
+
}, [value, locale]);
|
|
139
|
+
|
|
140
|
+
const clamp = (n: number | null): number | null => {
|
|
141
|
+
if (n === null) return null;
|
|
142
|
+
let res = n;
|
|
143
|
+
if (typeof min === "number") res = Math.max(res, min);
|
|
144
|
+
if (typeof max === "number") res = Math.min(res, max);
|
|
145
|
+
return res;
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
149
|
+
const raw = e.target.value;
|
|
150
|
+
setDisplayValue(raw);
|
|
151
|
+
|
|
152
|
+
// Parse for internal value
|
|
153
|
+
const parsed = parseNumberFromString(raw, allowNegative, locale);
|
|
154
|
+
const clamped = clamp(parsed);
|
|
155
|
+
setInternalNumber(clamped);
|
|
156
|
+
onValueChange?.(clamped);
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
const handleBlur = () => {
|
|
160
|
+
setDisplayValue(formatNumber(internalNumber, locale));
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
const handleFocus = () => {
|
|
164
|
+
// Optional: On focus, show raw value or keep as is?
|
|
165
|
+
// Often easier to edit without separators.
|
|
166
|
+
if (internalNumber !== null) {
|
|
167
|
+
// Simple string representation for editing
|
|
168
|
+
// But users might like seeing "100.000" and just changing one digit.
|
|
169
|
+
// Let's keep it simple: Just let them edit the string they see.
|
|
170
|
+
// If we want to strip separators on focus:
|
|
171
|
+
// setDisplayValue(internalNumber.toString())
|
|
172
|
+
// BUT current logic parseNumberFromString handles separators fine.
|
|
173
|
+
// So we just let them edit.
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
|
178
|
+
if (e.key === "ArrowUp" || e.key === "ArrowDown") {
|
|
179
|
+
e.preventDefault();
|
|
180
|
+
const current = internalNumber ?? 0;
|
|
181
|
+
const delta = e.key === "ArrowUp" ? step : -step;
|
|
182
|
+
const next = clamp(current + delta);
|
|
183
|
+
setInternalNumber(next);
|
|
184
|
+
setDisplayValue(formatNumber(next, locale));
|
|
185
|
+
onValueChange?.(next);
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
return (
|
|
190
|
+
<div className="relative flex items-center w-full">
|
|
191
|
+
<Input
|
|
192
|
+
type="text"
|
|
193
|
+
inputMode="decimal"
|
|
194
|
+
value={displayValue}
|
|
195
|
+
onChange={handleChange}
|
|
196
|
+
onBlur={handleBlur}
|
|
197
|
+
onFocus={handleFocus}
|
|
198
|
+
onKeyDown={handleKeyDown}
|
|
199
|
+
placeholder={placeholder}
|
|
200
|
+
className={`font-mono text-sm ${suffix ? "pr-12" : ""} ${className || ""}`}
|
|
201
|
+
{...inputProps}
|
|
202
|
+
/>
|
|
203
|
+
{suffix && (
|
|
204
|
+
<span className="pointer-events-none absolute right-3 text-xs text-muted-foreground">
|
|
205
|
+
{suffix}
|
|
206
|
+
</span>
|
|
207
|
+
)}
|
|
208
|
+
</div>
|
|
209
|
+
);
|
|
210
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Fragment } from "react";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { toJsxRuntime } from "hast-util-to-jsx-runtime";
|
|
4
|
+
import { codeToHast } from "shiki/bundle/web";
|
|
5
|
+
|
|
6
|
+
import type { JSX } from "react";
|
|
7
|
+
import type { BundledLanguage } from "shiki/bundle/web";
|
|
8
|
+
|
|
9
|
+
export async function highlight(code: string, lang: BundledLanguage) {
|
|
10
|
+
const out = await codeToHast(code, {
|
|
11
|
+
lang,
|
|
12
|
+
theme: "github-dark",
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
return toJsxRuntime(out, {
|
|
16
|
+
Fragment,
|
|
17
|
+
jsx,
|
|
18
|
+
jsxs,
|
|
19
|
+
}) as JSX.Element;
|
|
20
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as HoverCardPrimitive from "@radix-ui/react-hover-card";
|
|
4
|
+
|
|
5
|
+
import type { ComponentProps } from "react";
|
|
6
|
+
|
|
7
|
+
import { cn } from "../../utils";
|
|
8
|
+
|
|
9
|
+
export function HoverCard({
|
|
10
|
+
...props
|
|
11
|
+
}: ComponentProps<typeof HoverCardPrimitive.Root>) {
|
|
12
|
+
return <HoverCardPrimitive.Root data-slot="hover-card" {...props} />;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function HoverCardTrigger({
|
|
16
|
+
className,
|
|
17
|
+
...props
|
|
18
|
+
}: ComponentProps<typeof HoverCardPrimitive.Trigger>) {
|
|
19
|
+
return (
|
|
20
|
+
<HoverCardPrimitive.Trigger
|
|
21
|
+
data-slot="hover-card-trigger"
|
|
22
|
+
className={cn("cursor-pointer", className)}
|
|
23
|
+
{...props}
|
|
24
|
+
/>
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function HoverCardContent({
|
|
29
|
+
className,
|
|
30
|
+
align = "center",
|
|
31
|
+
sideOffset = 4,
|
|
32
|
+
...props
|
|
33
|
+
}: ComponentProps<typeof HoverCardPrimitive.Content>) {
|
|
34
|
+
return (
|
|
35
|
+
<HoverCardPrimitive.Portal data-slot="hover-card-portal">
|
|
36
|
+
<HoverCardPrimitive.Content
|
|
37
|
+
data-slot="hover-card-content"
|
|
38
|
+
align={align}
|
|
39
|
+
sideOffset={sideOffset}
|
|
40
|
+
className={cn(
|
|
41
|
+
"z-50 w-64 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-hidden data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
|
42
|
+
className,
|
|
43
|
+
)}
|
|
44
|
+
{...props}
|
|
45
|
+
/>
|
|
46
|
+
</HoverCardPrimitive.Portal>
|
|
47
|
+
);
|
|
48
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// @goerp/core/ui/data-display
|
|
2
|
+
// Data display components (Table, Card views, etc.)
|
|
3
|
+
|
|
4
|
+
"use client";
|
|
5
|
+
|
|
6
|
+
// Table definitions removed to avoid conflict with primitives
|
|
7
|
+
|
|
8
|
+
// Re-export DataTableColumnHeader, FormattedNumberInput, DataTablePagination
|
|
9
|
+
export { DataTableColumnHeader } from "./data-table-column-header";
|
|
10
|
+
export { FormattedNumberInput } from "./formatted-number-input";
|
|
11
|
+
export type { FormattedNumberInputProps } from "./formatted-number-input";
|
|
12
|
+
export { DataTablePagination } from "./data-table-pagination";
|
|
13
|
+
export { DataTableViewOptions } from "./data-table-view-options";
|
|
14
|
+
|
|
15
|
+
export * from "./formatted-number-input";
|
|
16
|
+
export * from "./data-table-pagination";
|
|
17
|
+
export * from "./avatar";
|
|
18
|
+
export * from "./kpi-card";
|
|
19
|
+
export * from "./compact-stat-bar";
|
|
20
|
+
export * from "./show-more-text";
|
|
21
|
+
export * from "./chart";
|
|
22
|
+
export * from "./carousel";
|
|
23
|
+
export * from "./accordion";
|
|
24
|
+
export * from "./kanban";
|
|
25
|
+
export * from "./aspect-ratio";
|
|
26
|
+
export * from "./bento-grid";
|
|
27
|
+
export * from "./code-block-highlight";
|
|
28
|
+
export * from "./collapsible";
|
|
29
|
+
export * from "./highlight";
|
|
30
|
+
export * from "./hover-card";
|
|
31
|
+
export * from "./iphone-15-pro";
|
|
32
|
+
export * from "./media-grid";
|
|
33
|
+
export * from "./safari";
|
|
34
|
+
// Re-export specific components from primitives to avoid duplication
|
|
35
|
+
export { Tabs, TabsList, TabsTrigger, TabsContent } from "../primitives";
|
|
36
|
+
|
|
37
|
+
export {
|
|
38
|
+
Table,
|
|
39
|
+
TableHeader,
|
|
40
|
+
TableBody,
|
|
41
|
+
TableFooter,
|
|
42
|
+
TableHead,
|
|
43
|
+
TableRow,
|
|
44
|
+
TableCell,
|
|
45
|
+
TableCaption,
|
|
46
|
+
} from "../primitives";
|
|
47
|
+
export * from "./timeline";
|
|
48
|
+
|
|
49
|
+
// DataTable - Reusable table component for standalone usage
|
|
50
|
+
export * from "./data-table";
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import type { SVGProps } from "react";
|
|
2
|
+
|
|
3
|
+
export interface Iphone15ProProps extends SVGProps<SVGSVGElement> {
|
|
4
|
+
width?: number;
|
|
5
|
+
height?: number;
|
|
6
|
+
imageSrc?: string;
|
|
7
|
+
videoSrc?: string;
|
|
8
|
+
id?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function Iphone15Pro({
|
|
12
|
+
width = 433,
|
|
13
|
+
height = 882,
|
|
14
|
+
imageSrc,
|
|
15
|
+
videoSrc,
|
|
16
|
+
id = "iphone-15-pro",
|
|
17
|
+
...props
|
|
18
|
+
}: Iphone15ProProps) {
|
|
19
|
+
const roundedCornersClipId = id + "-rounded-corners";
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<svg
|
|
23
|
+
width={width}
|
|
24
|
+
height={height}
|
|
25
|
+
viewBox={`0 0 ${width} ${height}`}
|
|
26
|
+
fill="none"
|
|
27
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
28
|
+
id={id}
|
|
29
|
+
{...props}
|
|
30
|
+
>
|
|
31
|
+
<path
|
|
32
|
+
d="M2 73C2 32.6832 34.6832 0 75 0H357C397.317 0 430 32.6832 430 73V809C430 849.317 397.317 882 357 882H75C34.6832 882 2 849.317 2 809V73Z"
|
|
33
|
+
className="fill-muted"
|
|
34
|
+
/>
|
|
35
|
+
<path
|
|
36
|
+
d="M0 171C0 170.448 0.447715 170 1 170H3V204H1C0.447715 204 0 203.552 0 203V171Z"
|
|
37
|
+
className="fill-muted"
|
|
38
|
+
/>
|
|
39
|
+
<path
|
|
40
|
+
d="M1 234C1 233.448 1.44772 233 2 233H3.5V300H2C1.44772 300 1 299.552 1 299V234Z"
|
|
41
|
+
className="fill-muted"
|
|
42
|
+
/>
|
|
43
|
+
<path
|
|
44
|
+
d="M1 319C1 318.448 1.44772 318 2 318H3.5V385H2C1.44772 385 1 384.552 1 384V319Z"
|
|
45
|
+
className="fill-muted"
|
|
46
|
+
/>
|
|
47
|
+
<path
|
|
48
|
+
d="M430 279H432C432.552 279 433 279.448 433 280V384C433 384.552 432.552 385 432 385H430V279Z"
|
|
49
|
+
className="fill-muted"
|
|
50
|
+
/>
|
|
51
|
+
<path
|
|
52
|
+
d="M6 74C6 35.3401 37.3401 4 76 4H356C394.66 4 426 35.3401 426 74V808C426 846.66 394.66 878 356 878H76C37.3401 878 6 846.66 6 808V74Z"
|
|
53
|
+
className="fill-background"
|
|
54
|
+
/>
|
|
55
|
+
<path
|
|
56
|
+
opacity="0.5"
|
|
57
|
+
d="M174 5H258V5.5C258 6.60457 257.105 7.5 256 7.5H176C174.895 7.5 174 6.60457 174 5.5V5Z"
|
|
58
|
+
className="fill-muted"
|
|
59
|
+
/>
|
|
60
|
+
<path
|
|
61
|
+
d="M21.25 75C21.25 44.2101 46.2101 19.25 77 19.25H355C385.79 19.25 410.75 44.2101 410.75 75V807C410.75 837.79 385.79 862.75 355 862.75H77C46.2101 862.75 21.25 837.79 21.25 807V75Z"
|
|
62
|
+
className="fill-muted stroke-muted stroke-[0.5]"
|
|
63
|
+
/>
|
|
64
|
+
|
|
65
|
+
{imageSrc && (
|
|
66
|
+
<image
|
|
67
|
+
href={imageSrc}
|
|
68
|
+
x="21.25"
|
|
69
|
+
y="19.25"
|
|
70
|
+
width="389.5"
|
|
71
|
+
height="843.5"
|
|
72
|
+
preserveAspectRatio="xMidYMid slice"
|
|
73
|
+
clipPath={`url(#${roundedCornersClipId})`}
|
|
74
|
+
/>
|
|
75
|
+
)}
|
|
76
|
+
{videoSrc && (
|
|
77
|
+
<foreignObject x="21.25" y="19.25" width="389.5" height="843.5">
|
|
78
|
+
<video
|
|
79
|
+
className="size-full overflow-hidden rounded-[55.75px] object-cover"
|
|
80
|
+
src={videoSrc}
|
|
81
|
+
autoPlay
|
|
82
|
+
loop
|
|
83
|
+
muted
|
|
84
|
+
playsInline
|
|
85
|
+
/>
|
|
86
|
+
</foreignObject>
|
|
87
|
+
)}
|
|
88
|
+
<path
|
|
89
|
+
d="M154 48.5C154 38.2827 162.283 30 172.5 30H259.5C269.717 30 278 38.2827 278 48.5C278 58.7173 269.717 67 259.5 67H172.5C162.283 67 154 58.7173 154 48.5Z"
|
|
90
|
+
className="fill-muted"
|
|
91
|
+
/>
|
|
92
|
+
<path
|
|
93
|
+
d="M249 48.5C249 42.701 253.701 38 259.5 38C265.299 38 270 42.701 270 48.5C270 54.299 265.299 59 259.5 59C253.701 59 249 54.299 249 48.5Z"
|
|
94
|
+
className="fill-muted"
|
|
95
|
+
/>
|
|
96
|
+
<path
|
|
97
|
+
d="M254 48.5C254 45.4624 256.462 43 259.5 43C262.538 43 265 45.4624 265 48.5C265 51.5376 262.538 54 259.5 54C256.462 54 254 51.5376 254 48.5Z"
|
|
98
|
+
className="fill-background"
|
|
99
|
+
/>
|
|
100
|
+
<defs>
|
|
101
|
+
<clipPath id={roundedCornersClipId}>
|
|
102
|
+
<rect
|
|
103
|
+
x="21.25"
|
|
104
|
+
y="19.25"
|
|
105
|
+
width="389.5"
|
|
106
|
+
height="843.5"
|
|
107
|
+
rx="55.75"
|
|
108
|
+
ry="55.75"
|
|
109
|
+
/>
|
|
110
|
+
</clipPath>
|
|
111
|
+
</defs>
|
|
112
|
+
</svg>
|
|
113
|
+
);
|
|
114
|
+
}
|