@airoom/nextmin-react 0.1.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/LICENSE +49 -0
- package/README.md +133 -0
- package/dist/auth/AuthPage.d.ts +1 -0
- package/dist/auth/AuthPage.js +23 -0
- package/dist/auth/ForgotPasswordForm.d.ts +1 -0
- package/dist/auth/ForgotPasswordForm.js +28 -0
- package/dist/auth/SignInForm.d.ts +6 -0
- package/dist/auth/SignInForm.js +38 -0
- package/dist/auth/SignUpForm.d.ts +3 -0
- package/dist/auth/SignUpForm.js +30 -0
- package/dist/components/AddressAutocomplete.d.ts +21 -0
- package/dist/components/AddressAutocomplete.js +182 -0
- package/dist/components/AdminApp.d.ts +1 -0
- package/dist/components/AdminApp.js +134 -0
- package/dist/components/ConfirmDialog.d.ts +12 -0
- package/dist/components/ConfirmDialog.js +6 -0
- package/dist/components/FileUploader.d.ts +32 -0
- package/dist/components/FileUploader.js +480 -0
- package/dist/components/NoAccess.d.ts +3 -0
- package/dist/components/NoAccess.js +5 -0
- package/dist/components/PasswordInput.d.ts +19 -0
- package/dist/components/PasswordInput.js +11 -0
- package/dist/components/PhoneInput.d.ts +23 -0
- package/dist/components/PhoneInput.js +147 -0
- package/dist/components/RefMultiSelect.d.ts +14 -0
- package/dist/components/RefMultiSelect.js +76 -0
- package/dist/components/RefSingleSelect.d.ts +17 -0
- package/dist/components/RefSingleSelect.js +52 -0
- package/dist/components/SchemaForm.d.ts +13 -0
- package/dist/components/SchemaForm.js +592 -0
- package/dist/components/SectionLoader.d.ts +3 -0
- package/dist/components/SectionLoader.js +7 -0
- package/dist/components/Sidebar.d.ts +1 -0
- package/dist/components/Sidebar.js +87 -0
- package/dist/components/TableFilters.d.ts +16 -0
- package/dist/components/TableFilters.js +69 -0
- package/dist/components/TableSkeleton.d.ts +7 -0
- package/dist/components/TableSkeleton.js +5 -0
- package/dist/hooks/useGoogleMapsKey.d.ts +5 -0
- package/dist/hooks/useGoogleMapsKey.js +16 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/lib/api.d.ts +31 -0
- package/dist/lib/api.js +94 -0
- package/dist/lib/auth.d.ts +23 -0
- package/dist/lib/auth.js +51 -0
- package/dist/lib/googleLoader.d.ts +1 -0
- package/dist/lib/googleLoader.js +25 -0
- package/dist/lib/schemaService.d.ts +2 -0
- package/dist/lib/schemaService.js +39 -0
- package/dist/lib/schemaUtils.d.ts +4 -0
- package/dist/lib/schemaUtils.js +18 -0
- package/dist/lib/types.d.ts +50 -0
- package/dist/lib/types.js +1 -0
- package/dist/nextmin.css +1 -0
- package/dist/providers/NextMinProvider.d.ts +5 -0
- package/dist/providers/NextMinProvider.js +30 -0
- package/dist/router/AdminRouteNormalizer.d.ts +1 -0
- package/dist/router/AdminRouteNormalizer.js +32 -0
- package/dist/router/NextMinRouter.d.ts +1 -0
- package/dist/router/NextMinRouter.js +99 -0
- package/dist/state/nextMinSlice.d.ts +14 -0
- package/dist/state/nextMinSlice.js +34 -0
- package/dist/state/schemaLive.d.ts +2 -0
- package/dist/state/schemaLive.js +19 -0
- package/dist/state/schemasSlice.d.ts +20 -0
- package/dist/state/schemasSlice.js +43 -0
- package/dist/state/sessionSlice.d.ts +10 -0
- package/dist/state/sessionSlice.js +18 -0
- package/dist/state/store.d.ts +28 -0
- package/dist/state/store.js +7 -0
- package/dist/views/CreateEditPage.d.ts +4 -0
- package/dist/views/CreateEditPage.js +64 -0
- package/dist/views/DashboardPage.d.ts +1 -0
- package/dist/views/DashboardPage.js +107 -0
- package/dist/views/ListPage.d.ts +5 -0
- package/dist/views/ListPage.js +76 -0
- package/dist/views/NextNotFound.d.ts +1 -0
- package/dist/views/NextNotFound.js +6 -0
- package/dist/views/ProfilePage.d.ts +1 -0
- package/dist/views/ProfilePage.js +193 -0
- package/dist/views/SettingsEdit.d.ts +2 -0
- package/dist/views/SettingsEdit.js +87 -0
- package/dist/views/list/DataTableHero.d.ts +22 -0
- package/dist/views/list/DataTableHero.js +350 -0
- package/dist/views/list/ListHeader.d.ts +8 -0
- package/dist/views/list/ListHeader.js +7 -0
- package/dist/views/list/Pagination.d.ts +8 -0
- package/dist/views/list/Pagination.js +5 -0
- package/dist/views/list/formatters.d.ts +2 -0
- package/dist/views/list/formatters.js +62 -0
- package/dist/views/list/useListData.d.ts +10 -0
- package/dist/views/list/useListData.js +79 -0
- package/package.json +51 -0
- package/tsconfig.json +18 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import Link from 'next/link';
|
|
4
|
+
import { Button, Divider } from '@heroui/react';
|
|
5
|
+
export function ListHeader({ title, createHref, hideCreate = false, loading, }) {
|
|
6
|
+
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between", children: [_jsx("h2", { className: "m-0 text-xl font-semibold", children: title }), _jsx("div", { className: "flex items-center gap-3", children: !hideCreate && (_jsx(Link, { href: createHref, children: _jsx(Button, { size: "sm", color: "primary", isDisabled: loading, children: "Create" }) })) })] }), _jsx(Divider, { className: "my-3" })] }));
|
|
7
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
export function Pagination({ page, pageCount, onPrev, onNext }) {
|
|
4
|
+
return (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("button", { onClick: onPrev, disabled: page === 0, children: "Prev" }), _jsxs("span", { children: ["Page ", page + 1, " / ", pageCount] }), _jsx("button", { onClick: onNext, disabled: page + 1 >= pageCount, children: "Next" })] }));
|
|
5
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
function isCreatedOrUpdated(key) {
|
|
2
|
+
const k = (key || '').toLowerCase().replace(/[^a-z]/g, '');
|
|
3
|
+
return k === 'createdat' || k === 'updatedat';
|
|
4
|
+
}
|
|
5
|
+
function tryParseDate(val) {
|
|
6
|
+
if (val == null)
|
|
7
|
+
return null;
|
|
8
|
+
// Handle numeric timestamps (ms or s)
|
|
9
|
+
if (typeof val === 'number') {
|
|
10
|
+
const ms = val < 1e11 ? val * 1000 : val; // heuristic: 10/13-digit
|
|
11
|
+
const d = new Date(ms);
|
|
12
|
+
return isNaN(d.getTime()) ? null : d;
|
|
13
|
+
}
|
|
14
|
+
// ISO string / Date
|
|
15
|
+
const d = new Date(val);
|
|
16
|
+
return isNaN(d.getTime()) ? null : d;
|
|
17
|
+
}
|
|
18
|
+
function formatLongDate(d) {
|
|
19
|
+
const day = d.getDate(); // no leading zero: "1 August, 2025"
|
|
20
|
+
const month = d.toLocaleString('en', { month: 'long' });
|
|
21
|
+
const year = d.getFullYear();
|
|
22
|
+
return `${day} ${month}, ${year}`; // "13 August, 2025"
|
|
23
|
+
}
|
|
24
|
+
export function formatAtom(v) {
|
|
25
|
+
if (v == null)
|
|
26
|
+
return '';
|
|
27
|
+
if (typeof v === 'string')
|
|
28
|
+
return v;
|
|
29
|
+
if (typeof v === 'number' || typeof v === 'boolean')
|
|
30
|
+
return String(v);
|
|
31
|
+
if (v && typeof v === 'object') {
|
|
32
|
+
// Common cases: show human-friendly fields if present
|
|
33
|
+
if (typeof v.name === 'string')
|
|
34
|
+
return v.name;
|
|
35
|
+
if (typeof v.title === 'string')
|
|
36
|
+
return v.title;
|
|
37
|
+
if (typeof v.id === 'string')
|
|
38
|
+
return v.id;
|
|
39
|
+
try {
|
|
40
|
+
return JSON.stringify(v);
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return String(v);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return String(v);
|
|
47
|
+
}
|
|
48
|
+
export function formatCell(val, key) {
|
|
49
|
+
if (val == null)
|
|
50
|
+
return '';
|
|
51
|
+
// Only format dates for createdAt / updatedAt columns
|
|
52
|
+
if (isCreatedOrUpdated(key)) {
|
|
53
|
+
const d = tryParseDate(val);
|
|
54
|
+
if (d)
|
|
55
|
+
return formatLongDate(d);
|
|
56
|
+
}
|
|
57
|
+
if (Array.isArray(val))
|
|
58
|
+
return val.map(formatAtom).join(', ');
|
|
59
|
+
if (typeof val === 'object')
|
|
60
|
+
return formatAtom(val);
|
|
61
|
+
return String(val);
|
|
62
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type ListParams } from '../../lib/api';
|
|
2
|
+
export type ListState<T = unknown> = {
|
|
3
|
+
rows: T[];
|
|
4
|
+
total: number;
|
|
5
|
+
loading: boolean;
|
|
6
|
+
forbidden: boolean;
|
|
7
|
+
err: string | null;
|
|
8
|
+
};
|
|
9
|
+
export declare function useListData<T = unknown>(model: string, page: number, // 1-based
|
|
10
|
+
pageSize: number, filters?: ListParams): [ListState<T>, () => Promise<void>];
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
3
|
+
import { api, ApiError } from '../../lib/api';
|
|
4
|
+
export function useListData(model, page, // 1-based
|
|
5
|
+
pageSize, filters = {}) {
|
|
6
|
+
const [state, setState] = useState({
|
|
7
|
+
rows: [],
|
|
8
|
+
total: 0,
|
|
9
|
+
loading: true,
|
|
10
|
+
forbidden: false,
|
|
11
|
+
err: null,
|
|
12
|
+
});
|
|
13
|
+
const cancelledRef = useRef(false);
|
|
14
|
+
const filtersKey = JSON.stringify(filters);
|
|
15
|
+
const load = useCallback(async () => {
|
|
16
|
+
setState((s) => ({ ...s, loading: true, forbidden: false, err: null }));
|
|
17
|
+
try {
|
|
18
|
+
const res = await api.list(model, Math.max(0, page - 1), pageSize, filters);
|
|
19
|
+
if (cancelledRef.current)
|
|
20
|
+
return;
|
|
21
|
+
// Support both {data, pagination} and {rows, total}
|
|
22
|
+
const rows = res?.data ??
|
|
23
|
+
(Array.isArray(res?.rows) ? res.rows : []) ??
|
|
24
|
+
[];
|
|
25
|
+
const totalServer = res?.pagination?.totalRows ??
|
|
26
|
+
(typeof res?.total === 'number'
|
|
27
|
+
? res.total
|
|
28
|
+
: undefined);
|
|
29
|
+
const total = typeof totalServer === 'number'
|
|
30
|
+
? totalServer
|
|
31
|
+
: Array.isArray(rows)
|
|
32
|
+
? rows.length
|
|
33
|
+
: 0;
|
|
34
|
+
setState({
|
|
35
|
+
rows: Array.isArray(rows) ? rows : [],
|
|
36
|
+
total,
|
|
37
|
+
loading: false,
|
|
38
|
+
forbidden: false,
|
|
39
|
+
err: null,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
catch (e) {
|
|
43
|
+
if (cancelledRef.current)
|
|
44
|
+
return;
|
|
45
|
+
if (e instanceof ApiError && (e.status === 401 || e.status === 403)) {
|
|
46
|
+
setState({
|
|
47
|
+
rows: [],
|
|
48
|
+
total: 0,
|
|
49
|
+
loading: false,
|
|
50
|
+
forbidden: true,
|
|
51
|
+
err: e.message ||
|
|
52
|
+
(e.status === 401 ? 'Not authenticated' : 'Not permitted'),
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
const msg = e?.message?.toString() ??
|
|
57
|
+
'Failed to load data';
|
|
58
|
+
setState({
|
|
59
|
+
rows: [],
|
|
60
|
+
total: 0,
|
|
61
|
+
loading: false,
|
|
62
|
+
forbidden: false,
|
|
63
|
+
err: msg,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}, [model, page, pageSize, filtersKey]);
|
|
68
|
+
useEffect(() => {
|
|
69
|
+
cancelledRef.current = false;
|
|
70
|
+
void load();
|
|
71
|
+
return () => {
|
|
72
|
+
cancelledRef.current = true;
|
|
73
|
+
};
|
|
74
|
+
}, [load]);
|
|
75
|
+
const refetch = useCallback(async () => {
|
|
76
|
+
await load();
|
|
77
|
+
}, [load]);
|
|
78
|
+
return [state, refetch];
|
|
79
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@airoom/nextmin-react",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"default": "./dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"./styles.css": "./dist/nextmin.css"
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"package.json",
|
|
19
|
+
"tsconfig.json",
|
|
20
|
+
"LICENSE",
|
|
21
|
+
"README.md"
|
|
22
|
+
],
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build": "rm -rf dist && tsc -p tsconfig.json && npm run build:css",
|
|
25
|
+
"build:css": "tailwindcss -c tailwind.config.js -i ./src/styles.css -o ./dist/nextmin.css --minify",
|
|
26
|
+
"dev": "tsc -w -p tsconfig.json",
|
|
27
|
+
"prepublishOnly": "npm run build && npm pack --dry-run | (! grep -E '\\bsrc/|\\.map$')"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@googlemaps/js-api-loader": "^1.16.10",
|
|
31
|
+
"@heroui/react": "^2.8.2",
|
|
32
|
+
"@heroui/use-infinite-scroll": "^2.2.10",
|
|
33
|
+
"@internationalized/date": "^3.9.0",
|
|
34
|
+
"@reduxjs/toolkit": "^2.8.2",
|
|
35
|
+
"framer-motion": "^12.23.12",
|
|
36
|
+
"react": "*",
|
|
37
|
+
"react-dom": "*",
|
|
38
|
+
"react-redux": ">=9",
|
|
39
|
+
"socket.io-client": "^4.7.5"
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@types/google.maps": "^3.58.1",
|
|
43
|
+
"autoprefixer": "^10.4.21",
|
|
44
|
+
"postcss": "^8.5.6",
|
|
45
|
+
"tailwindcss": "^3.4.17"
|
|
46
|
+
},
|
|
47
|
+
"sideEffects": false,
|
|
48
|
+
"publishConfig": {
|
|
49
|
+
"access": "public"
|
|
50
|
+
}
|
|
51
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../../tsconfig.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"target": "ES2020",
|
|
5
|
+
"types": ["google.maps", "node"],
|
|
6
|
+
"module": "ESNext",
|
|
7
|
+
"moduleResolution": "Bundler",
|
|
8
|
+
"jsx": "react-jsx",
|
|
9
|
+
"declaration": true,
|
|
10
|
+
"outDir": "dist",
|
|
11
|
+
"rootDir": "src",
|
|
12
|
+
"esModuleInterop": true,
|
|
13
|
+
"skipLibCheck": true,
|
|
14
|
+
"resolveJsonModule": true,
|
|
15
|
+
"sourceMap": false
|
|
16
|
+
},
|
|
17
|
+
"include": ["src"]
|
|
18
|
+
}
|