mobigrid-module 1.0.0
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/dist/components/CustomTable/CustomTable.d.ts +8 -0
- package/dist/components/CustomTable/Pagination.d.ts +8 -0
- package/dist/components/Icon.d.ts +6 -0
- package/dist/components/Layout/PageHeader.d.ts +13 -0
- package/dist/components/ui/alert.d.ts +8 -0
- package/dist/components/ui/button.d.ts +11 -0
- package/dist/components/ui/calendar.d.ts +8 -0
- package/dist/components/ui/date-picker-with-range.d.ts +7 -0
- package/dist/components/ui/dialog.d.ts +19 -0
- package/dist/components/ui/input.d.ts +3 -0
- package/dist/components/ui/pagination.d.ts +28 -0
- package/dist/components/ui/popover.d.ts +7 -0
- package/dist/components/ui/select.d.ts +13 -0
- package/dist/components/ui/sonner.d.ts +5 -0
- package/dist/components/ui/table.d.ts +10 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.esm.js +70 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/index.tsx +70 -0
- package/dist/index.tsx.map +1 -0
- package/dist/lib/utils.d.ts +2 -0
- package/package.json +67 -0
- package/rollup.config.js +35 -0
- package/src/components/CustomTable/CustomTable.tsx +182 -0
- package/src/components/CustomTable/Pagination.tsx +136 -0
- package/src/components/Icon.tsx +27 -0
- package/src/components/Layout/PageHeader.tsx +270 -0
- package/src/components/ui/alert.tsx +59 -0
- package/src/components/ui/button.tsx +57 -0
- package/src/components/ui/calendar.tsx +70 -0
- package/src/components/ui/date-picker-with-range.tsx +167 -0
- package/src/components/ui/dialog.tsx +120 -0
- package/src/components/ui/input.tsx +22 -0
- package/src/components/ui/pagination.tsx +117 -0
- package/src/components/ui/popover.tsx +31 -0
- package/src/components/ui/select.tsx +157 -0
- package/src/components/ui/sonner.tsx +30 -0
- package/src/components/ui/table.tsx +120 -0
- package/src/index.tsx +252 -0
- package/src/lib/utils.ts +6 -0
- package/tsconfig.json +27 -0
- package/vite.config.ts +6 -0
package/package.json
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
{
|
2
|
+
"name": "mobigrid-module",
|
3
|
+
"version": "1.0.0",
|
4
|
+
"description": "",
|
5
|
+
"main": "index.tsx",
|
6
|
+
"type": "module",
|
7
|
+
"scripts": {
|
8
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
9
|
+
"build": "rollup -c"
|
10
|
+
},
|
11
|
+
"keywords": [],
|
12
|
+
"author": "",
|
13
|
+
"license": "ISC",
|
14
|
+
"dependencies": {
|
15
|
+
"@radix-ui/react-checkbox": "^1.1.2",
|
16
|
+
"@radix-ui/react-dialog": "^1.1.2",
|
17
|
+
"@radix-ui/react-popover": "^1.1.2",
|
18
|
+
"@radix-ui/react-select": "^2.1.2",
|
19
|
+
"@radix-ui/react-slot": "^1.1.0",
|
20
|
+
"@rollup/plugin-commonjs": "^28.0.1",
|
21
|
+
"@rollup/plugin-node-resolve": "^15.3.0",
|
22
|
+
"@rollup/plugin-typescript": "^12.1.1",
|
23
|
+
"@tanstack/react-table": "^8.20.5",
|
24
|
+
"axios": "^1.7.8",
|
25
|
+
"date-fns": "^4.1.0",
|
26
|
+
"lucide-react": "^0.462.0",
|
27
|
+
"next-themes": "^0.4.3",
|
28
|
+
"react-day-picker": "8.10.1",
|
29
|
+
"react-feather": "^2.0.10",
|
30
|
+
"rollup-plugin-peer-deps-external": "^2.2.4",
|
31
|
+
"rollup-plugin-terser": "^7.0.2",
|
32
|
+
"sonner": "^1.7.0",
|
33
|
+
"tslib": "^2.8.1"
|
34
|
+
},
|
35
|
+
"devDependencies": {
|
36
|
+
"@babel/cli": "^7.25.9",
|
37
|
+
"@babel/core": "^7.26.0",
|
38
|
+
"@babel/preset-env": "^7.26.0",
|
39
|
+
"@babel/preset-react": "^7.25.9",
|
40
|
+
"@babel/preset-typescript": "^7.26.0",
|
41
|
+
"@eslint/js": "^9.13.0",
|
42
|
+
"@rollup/plugin-json": "^6.1.0",
|
43
|
+
"@shadcn/ui": "^0.0.4",
|
44
|
+
"@types/node": "^22.10.1",
|
45
|
+
"@types/react": "^18.3.12",
|
46
|
+
"@types/react-dom": "^18.3.1",
|
47
|
+
"@vitejs/plugin-react": "^4.3.3",
|
48
|
+
"autoprefixer": "^10.4.20",
|
49
|
+
"class-variance-authority": "^0.7.0",
|
50
|
+
"clsx": "^2.1.1",
|
51
|
+
"eslint": "^9.13.0",
|
52
|
+
"eslint-plugin-react-hooks": "^5.0.0",
|
53
|
+
"eslint-plugin-react-refresh": "^0.4.14",
|
54
|
+
"globals": "^15.11.0",
|
55
|
+
"lucide-react": "^0.456.0",
|
56
|
+
"postcss": "^8.4.47",
|
57
|
+
"react": "^18.3.1",
|
58
|
+
"react-dom": "^18.3.1",
|
59
|
+
"tailwind-merge": "^2.5.5",
|
60
|
+
"tailwindcss": "^3.4.14",
|
61
|
+
"tailwindcss-animate": "^1.0.7",
|
62
|
+
"tsup": "^8.3.5",
|
63
|
+
"typescript": "^5.7.2",
|
64
|
+
"typescript-eslint": "^8.11.0",
|
65
|
+
"vite": "^5.4.10"
|
66
|
+
}
|
67
|
+
}
|
package/rollup.config.js
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
import resolve from "@rollup/plugin-node-resolve";
|
2
|
+
import commonjs from "@rollup/plugin-commonjs";
|
3
|
+
import peerDepsExternal from "rollup-plugin-peer-deps-external";
|
4
|
+
import typescript from "@rollup/plugin-typescript";
|
5
|
+
import { terser } from "rollup-plugin-terser";
|
6
|
+
import json from '@rollup/plugin-json';
|
7
|
+
|
8
|
+
export default {
|
9
|
+
input: "src/index.tsx",
|
10
|
+
output: [
|
11
|
+
{
|
12
|
+
file: "dist/index.tsx",
|
13
|
+
format: "cjs",
|
14
|
+
sourcemap: true,
|
15
|
+
},
|
16
|
+
{
|
17
|
+
file: "dist/index.esm.js",
|
18
|
+
format: "esm",
|
19
|
+
sourcemap: true,
|
20
|
+
},
|
21
|
+
],
|
22
|
+
plugins: [
|
23
|
+
peerDepsExternal(),
|
24
|
+
resolve(),
|
25
|
+
commonjs(),
|
26
|
+
typescript({
|
27
|
+
tsconfig: "./tsconfig.json",
|
28
|
+
outDir: "./dist",
|
29
|
+
declarationDir: "./dist"
|
30
|
+
}),
|
31
|
+
terser(),
|
32
|
+
json(),
|
33
|
+
],
|
34
|
+
external: ["react", "react-dom"],
|
35
|
+
};
|
@@ -0,0 +1,182 @@
|
|
1
|
+
import React from "react";
|
2
|
+
import {
|
3
|
+
flexRender,
|
4
|
+
getCoreRowModel,
|
5
|
+
useReactTable,
|
6
|
+
} from "@tanstack/react-table";
|
7
|
+
import {
|
8
|
+
Table,
|
9
|
+
TableBody,
|
10
|
+
TableCell,
|
11
|
+
TableHead,
|
12
|
+
TableHeader,
|
13
|
+
TableRow,
|
14
|
+
} from "../../components/ui/table";
|
15
|
+
import { format } from "date-fns";
|
16
|
+
import Icon from "../Icon";
|
17
|
+
|
18
|
+
interface TableProps {
|
19
|
+
data: any[];
|
20
|
+
columns: any[];
|
21
|
+
isLoading: boolean;
|
22
|
+
}
|
23
|
+
|
24
|
+
export function CustomTable({ data, columns, isLoading }: TableProps) {
|
25
|
+
const table = useReactTable({
|
26
|
+
data: isLoading ? [] : data,
|
27
|
+
columns: columns.map((col) => ({
|
28
|
+
accessorKey: col.key,
|
29
|
+
header: col.title,
|
30
|
+
cell: (info) => {
|
31
|
+
if (col.key === "ACTIONS_BUTTONS") {
|
32
|
+
return (
|
33
|
+
<div className="flex gap-2 justify-end">
|
34
|
+
{col.actions?.map((action: any, index: number) => (
|
35
|
+
<button
|
36
|
+
key={index}
|
37
|
+
className={`inline-flex items-center px-2 py-1 text-sm rounded-full transition-colors duration-200 ${
|
38
|
+
index === 0
|
39
|
+
? "text-blue-700 bg-blue-50 border border-blue-200 hover:bg-blue-100"
|
40
|
+
: index === 1
|
41
|
+
? "text-green-700 bg-green-50 border border-green-200 hover:bg-green-100"
|
42
|
+
: index === 2
|
43
|
+
? "text-purple-700 bg-purple-50 border border-purple-200 hover:bg-purple-100"
|
44
|
+
: index === 3
|
45
|
+
? "text-orange-700 bg-orange-50 border border-orange-200 hover:bg-orange-100"
|
46
|
+
: "text-gray-700 bg-gray-50 border border-gray-200 hover:bg-gray-100"
|
47
|
+
}`}
|
48
|
+
onClick={() => {
|
49
|
+
console.log(action.action, info.row.original);
|
50
|
+
}}
|
51
|
+
>
|
52
|
+
{action.icon && (
|
53
|
+
<svg
|
54
|
+
xmlns="http://www.w3.org/2000/svg"
|
55
|
+
width="16"
|
56
|
+
height="16"
|
57
|
+
viewBox="0 0 24 24"
|
58
|
+
fill="none"
|
59
|
+
stroke="currentColor"
|
60
|
+
strokeWidth="2"
|
61
|
+
strokeLinecap="round"
|
62
|
+
strokeLinejoin="round"
|
63
|
+
className="mr-2"
|
64
|
+
>
|
65
|
+
<Icon name={action.icon.replace(/^icon-/, '')} className="mr-2" />
|
66
|
+
</svg>
|
67
|
+
)}
|
68
|
+
{action.label}
|
69
|
+
</button>
|
70
|
+
))}
|
71
|
+
</div>
|
72
|
+
);
|
73
|
+
}
|
74
|
+
if (col.type === "status") {
|
75
|
+
const statusColors: { [key: string]: string } = {
|
76
|
+
PENDING: "bg-yellow-100 text-yellow-800",
|
77
|
+
PAID: "bg-green-100 text-green-800",
|
78
|
+
CANCELLED: "bg-red-100 text-red-800",
|
79
|
+
CANCEL_STARTED: "bg-orange-100 text-orange-800",
|
80
|
+
ECHEC: "bg-gray-100 text-gray-800",
|
81
|
+
};
|
82
|
+
const status = info.getValue() as string;
|
83
|
+
return (
|
84
|
+
<div className="flex items-center justify-center">
|
85
|
+
<span
|
86
|
+
className={`px-2 py-1 rounded-full text-xs font-medium text-center ${
|
87
|
+
statusColors[status] || "bg-gray-100 text-gray-800"
|
88
|
+
}`}
|
89
|
+
>
|
90
|
+
{status || "-"}
|
91
|
+
</span>
|
92
|
+
</div>
|
93
|
+
);
|
94
|
+
}
|
95
|
+
if (col.type === "money") {
|
96
|
+
return `${info.getValue()} ${col.currency}`;
|
97
|
+
}
|
98
|
+
if (col.type === "date") {
|
99
|
+
return format(new Date(info.getValue() as string), "dd-MM-yyyy");
|
100
|
+
}
|
101
|
+
if (col.scroll) {
|
102
|
+
return (
|
103
|
+
<div className="max-h-24 overflow-y-auto">
|
104
|
+
{info.getValue()}
|
105
|
+
</div>
|
106
|
+
);
|
107
|
+
}
|
108
|
+
return info.getValue();
|
109
|
+
},
|
110
|
+
})),
|
111
|
+
getCoreRowModel: getCoreRowModel(),
|
112
|
+
enableMultiRowSelection: true,
|
113
|
+
enableRowSelection: true,
|
114
|
+
state: {
|
115
|
+
rowSelection: {},
|
116
|
+
},
|
117
|
+
});
|
118
|
+
|
119
|
+
return (
|
120
|
+
<Table className="border border-gray-200 rounded-md">
|
121
|
+
<TableHeader className="bg-gray-50">
|
122
|
+
{table.getHeaderGroups().map((headerGroup) => (
|
123
|
+
<TableRow key={headerGroup.id}>
|
124
|
+
{headerGroup.headers.map((header) => (
|
125
|
+
<TableHead key={header.id} className="font-medium text-gray-800 py-2">
|
126
|
+
{flexRender(
|
127
|
+
header.column.columnDef.header,
|
128
|
+
header.getContext()
|
129
|
+
)}
|
130
|
+
</TableHead>
|
131
|
+
))}
|
132
|
+
</TableRow>
|
133
|
+
))}
|
134
|
+
</TableHeader>
|
135
|
+
<TableBody>
|
136
|
+
{isLoading ? (
|
137
|
+
<TableRow>
|
138
|
+
<TableCell colSpan={columns.length} className="text-center py-4">
|
139
|
+
<svg
|
140
|
+
className="animate-spin h-5 w-5 mx-auto mb-2"
|
141
|
+
xmlns="http://www.w3.org/2000/svg"
|
142
|
+
fill="none"
|
143
|
+
viewBox="0 0 24 24"
|
144
|
+
>
|
145
|
+
<circle
|
146
|
+
className="opacity-25"
|
147
|
+
cx="12"
|
148
|
+
cy="12"
|
149
|
+
r="10"
|
150
|
+
stroke="currentColor"
|
151
|
+
strokeWidth="4"
|
152
|
+
></circle>
|
153
|
+
<path
|
154
|
+
className="opacity-75"
|
155
|
+
fill="currentColor"
|
156
|
+
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
157
|
+
></path>
|
158
|
+
</svg>
|
159
|
+
Chargement des données...
|
160
|
+
</TableCell>
|
161
|
+
</TableRow>
|
162
|
+
) : !data || data.length === 0 ? (
|
163
|
+
<TableRow>
|
164
|
+
<TableCell colSpan={columns.length} className="text-center py-4">
|
165
|
+
Aucune donnée disponible
|
166
|
+
</TableCell>
|
167
|
+
</TableRow>
|
168
|
+
) : (
|
169
|
+
table.getRowModel().rows.map((row) => (
|
170
|
+
<TableRow key={row.id} className="bg-white hover:bg-gray-100">
|
171
|
+
{row.getVisibleCells().map((cell) => (
|
172
|
+
<TableCell key={cell.id}>
|
173
|
+
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
174
|
+
</TableCell>
|
175
|
+
))}
|
176
|
+
</TableRow>
|
177
|
+
))
|
178
|
+
)}
|
179
|
+
</TableBody>
|
180
|
+
</Table>
|
181
|
+
);
|
182
|
+
}
|
@@ -0,0 +1,136 @@
|
|
1
|
+
import React from "react";
|
2
|
+
import {
|
3
|
+
Pagination,
|
4
|
+
PaginationContent,
|
5
|
+
PaginationEllipsis,
|
6
|
+
PaginationItem,
|
7
|
+
PaginationLink,
|
8
|
+
PaginationNext,
|
9
|
+
PaginationPrevious,
|
10
|
+
} from "../../components/ui/pagination";
|
11
|
+
|
12
|
+
interface PaginationProps {
|
13
|
+
currentPage: number;
|
14
|
+
totalPages: number;
|
15
|
+
onPageChange: (page: number) => void;
|
16
|
+
}
|
17
|
+
|
18
|
+
export default function ({
|
19
|
+
currentPage,
|
20
|
+
totalPages,
|
21
|
+
onPageChange,
|
22
|
+
}: PaginationProps) {
|
23
|
+
const renderPaginationItems = () => {
|
24
|
+
const items = [];
|
25
|
+
|
26
|
+
// Previous button
|
27
|
+
items.push(
|
28
|
+
<PaginationItem key="prev">
|
29
|
+
<PaginationPrevious
|
30
|
+
href="#"
|
31
|
+
onClick={(e) => {
|
32
|
+
e.preventDefault();
|
33
|
+
if (currentPage > 1) onPageChange(currentPage - 1);
|
34
|
+
}}
|
35
|
+
/>
|
36
|
+
</PaginationItem>
|
37
|
+
);
|
38
|
+
|
39
|
+
// First page
|
40
|
+
items.push(
|
41
|
+
<PaginationItem key={1}>
|
42
|
+
<PaginationLink
|
43
|
+
href="#"
|
44
|
+
isActive={currentPage === 1}
|
45
|
+
onClick={(e) => {
|
46
|
+
e.preventDefault();
|
47
|
+
onPageChange(1);
|
48
|
+
}}
|
49
|
+
>
|
50
|
+
1
|
51
|
+
</PaginationLink>
|
52
|
+
</PaginationItem>
|
53
|
+
);
|
54
|
+
|
55
|
+
// Calculate visible page range
|
56
|
+
let startPage = Math.max(2, currentPage - 1);
|
57
|
+
let endPage = Math.min(totalPages - 1, currentPage + 1);
|
58
|
+
|
59
|
+
// Add ellipsis after first page if needed
|
60
|
+
if (startPage > 2) {
|
61
|
+
items.push(
|
62
|
+
<PaginationItem key="ellipsis1">
|
63
|
+
<PaginationEllipsis />
|
64
|
+
</PaginationItem>
|
65
|
+
);
|
66
|
+
}
|
67
|
+
|
68
|
+
// Add middle pages
|
69
|
+
for (let i = startPage; i <= endPage; i++) {
|
70
|
+
items.push(
|
71
|
+
<PaginationItem key={i}>
|
72
|
+
<PaginationLink
|
73
|
+
href="#"
|
74
|
+
isActive={currentPage === i}
|
75
|
+
onClick={(e) => {
|
76
|
+
e.preventDefault();
|
77
|
+
onPageChange(i);
|
78
|
+
}}
|
79
|
+
>
|
80
|
+
{i}
|
81
|
+
</PaginationLink>
|
82
|
+
</PaginationItem>
|
83
|
+
);
|
84
|
+
}
|
85
|
+
|
86
|
+
// Add ellipsis before last page if needed
|
87
|
+
if (endPage < totalPages - 1) {
|
88
|
+
items.push(
|
89
|
+
<PaginationItem key="ellipsis2">
|
90
|
+
<PaginationEllipsis />
|
91
|
+
</PaginationItem>
|
92
|
+
);
|
93
|
+
}
|
94
|
+
|
95
|
+
// Last page (if not already included)
|
96
|
+
if (totalPages > 1) {
|
97
|
+
items.push(
|
98
|
+
<PaginationItem key={totalPages}>
|
99
|
+
<PaginationLink
|
100
|
+
href="#"
|
101
|
+
isActive={currentPage === totalPages}
|
102
|
+
onClick={(e) => {
|
103
|
+
e.preventDefault();
|
104
|
+
onPageChange(totalPages);
|
105
|
+
}}
|
106
|
+
>
|
107
|
+
{totalPages}
|
108
|
+
</PaginationLink>
|
109
|
+
</PaginationItem>
|
110
|
+
);
|
111
|
+
}
|
112
|
+
|
113
|
+
// Next button
|
114
|
+
items.push(
|
115
|
+
<PaginationItem key="next">
|
116
|
+
<PaginationNext
|
117
|
+
href="#"
|
118
|
+
onClick={(e) => {
|
119
|
+
e.preventDefault();
|
120
|
+
if (currentPage < totalPages) onPageChange(currentPage + 1);
|
121
|
+
}}
|
122
|
+
/>
|
123
|
+
</PaginationItem>
|
124
|
+
);
|
125
|
+
|
126
|
+
return items;
|
127
|
+
};
|
128
|
+
|
129
|
+
return (
|
130
|
+
<Pagination>
|
131
|
+
<PaginationContent>
|
132
|
+
{renderPaginationItems()}
|
133
|
+
</PaginationContent>
|
134
|
+
</Pagination>
|
135
|
+
);
|
136
|
+
}
|
@@ -0,0 +1,27 @@
|
|
1
|
+
import React from "react";
|
2
|
+
import * as FeatherIcons from 'react-feather';
|
3
|
+
import { IconProps as FeatherIconProps } from 'react-feather';
|
4
|
+
|
5
|
+
interface IconProps extends Omit<FeatherIconProps, 'ref'> {
|
6
|
+
name: string;
|
7
|
+
}
|
8
|
+
|
9
|
+
const Icon = ({ name, ...props }: IconProps): JSX.Element | null => {
|
10
|
+
const formatIconName = (iconName: string): string => {
|
11
|
+
return iconName
|
12
|
+
.split(/[-_]/)
|
13
|
+
.map(part => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase())
|
14
|
+
.join('');
|
15
|
+
};
|
16
|
+
|
17
|
+
const IconComponent = FeatherIcons[formatIconName(name) as keyof typeof FeatherIcons];
|
18
|
+
|
19
|
+
if (!IconComponent) {
|
20
|
+
console.warn(`Icon "${name}" not found in react-feather library`);
|
21
|
+
return null;
|
22
|
+
}
|
23
|
+
|
24
|
+
return <IconComponent {...props} />;
|
25
|
+
};
|
26
|
+
|
27
|
+
export default Icon;
|