Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c6cdd98b56 |
@ -7,146 +7,189 @@ import LayoutAuthenticated from '../layouts/Authenticated'
|
||||
import SectionMain from '../components/SectionMain'
|
||||
import SectionTitleLineWithButton from '../components/SectionTitleLineWithButton'
|
||||
import BaseIcon from "../components/BaseIcon";
|
||||
import BaseButton from "../components/BaseButton";
|
||||
import { getPageTitle } from '../config'
|
||||
import Link from "next/link";
|
||||
|
||||
import { hasPermission } from "../helpers/userPermissions";
|
||||
import { fetchWidgets } from '../stores/roles/rolesSlice';
|
||||
import { WidgetCreator } from '../components/WidgetCreator/WidgetCreator';
|
||||
import { SmartWidget } from '../components/SmartWidget/SmartWidget';
|
||||
|
||||
import { useAppDispatch, useAppSelector } from '../stores/hooks';
|
||||
|
||||
const EntityCard = ({ entity, label, count, iconPath, iconsColor, corners, cardsStyle }: any) => (
|
||||
<Link href={`/${entity}/${entity}-list`}>
|
||||
<div className={`${corners !== 'rounded-full' ? corners : 'rounded-3xl'} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6 hover:shadow-lg transition-all duration-200 transform hover:-translate-y-1`}>
|
||||
<div className="flex justify-between align-center">
|
||||
<div>
|
||||
<div className="text-lg leading-tight text-gray-500 dark:text-gray-400 font-medium">
|
||||
{label}
|
||||
</div>
|
||||
<div className="text-3xl leading-tight font-bold mt-1">
|
||||
{count}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<BaseIcon
|
||||
className={`${iconsColor}`}
|
||||
w="w-12"
|
||||
h="h-12"
|
||||
size={40}
|
||||
path={iconPath || icon.mdiTable}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
)
|
||||
|
||||
const Dashboard = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const iconsColor = useAppSelector((state) => state.style.iconsColor);
|
||||
const corners = useAppSelector((state) => state.style.corners);
|
||||
const cardsStyle = useAppSelector((state) => state.style.cardsStyle);
|
||||
|
||||
const loadingMessage = 'Loading...';
|
||||
const loadingMessage = '...';
|
||||
|
||||
|
||||
const [users, setUsers] = React.useState(loadingMessage);
|
||||
const [roles, setRoles] = React.useState(loadingMessage);
|
||||
const [permissions, setPermissions] = React.useState(loadingMessage);
|
||||
const [tenants, setTenants] = React.useState(loadingMessage);
|
||||
const [user_roles, setUser_roles] = React.useState(loadingMessage);
|
||||
const [modules, setModules] = React.useState(loadingMessage);
|
||||
const [tenant_modules, setTenant_modules] = React.useState(loadingMessage);
|
||||
const [plans, setPlans] = React.useState(loadingMessage);
|
||||
const [subscriptions, setSubscriptions] = React.useState(loadingMessage);
|
||||
const [activation_codes, setActivation_codes] = React.useState(loadingMessage);
|
||||
const [licenses, setLicenses] = React.useState(loadingMessage);
|
||||
const [audit_logs, setAudit_logs] = React.useState(loadingMessage);
|
||||
const [products, setProducts] = React.useState(loadingMessage);
|
||||
const [stock_movements, setStock_movements] = React.useState(loadingMessage);
|
||||
const [warehouses, setWarehouses] = React.useState(loadingMessage);
|
||||
const [warehouse_locations, setWarehouse_locations] = React.useState(loadingMessage);
|
||||
const [customers, setCustomers] = React.useState(loadingMessage);
|
||||
const [suppliers, setSuppliers] = React.useState(loadingMessage);
|
||||
const [invoices, setInvoices] = React.useState(loadingMessage);
|
||||
const [invoice_items, setInvoice_items] = React.useState(loadingMessage);
|
||||
const [payments, setPayments] = React.useState(loadingMessage);
|
||||
const [expenses, setExpenses] = React.useState(loadingMessage);
|
||||
const [equipment_assets, setEquipment_assets] = React.useState(loadingMessage);
|
||||
const [orders, setOrders] = React.useState(loadingMessage);
|
||||
const [order_items, setOrder_items] = React.useState(loadingMessage);
|
||||
|
||||
|
||||
const [widgetsRole, setWidgetsRole] = React.useState({
|
||||
role: { value: '', label: '' },
|
||||
});
|
||||
const [counts, setCounts] = React.useState<Record<string, any>>({});
|
||||
const { currentUser } = useAppSelector((state) => state.auth);
|
||||
const { isFetchingQuery } = useAppSelector((state) => state.openAi);
|
||||
|
||||
const { rolesWidgets, loading } = useAppSelector((state) => state.roles);
|
||||
const [widgetsRole, setWidgetsRole] = React.useState({ role: { value: '', label: '' } });
|
||||
|
||||
const entities = [
|
||||
{ key: 'products', label: 'Products', icon: icon.mdiPackageVariant, module: 'Inventory & Storage' },
|
||||
{ key: 'stock_movements', label: 'Stock Movements', icon: icon.mdiSwapHorizontal, module: 'Inventory & Storage' },
|
||||
{ key: 'warehouses', label: 'Warehouses', icon: icon.mdiWarehouse, module: 'Inventory & Storage' },
|
||||
{ key: 'warehouse_locations', label: 'Warehouse Locations', icon: icon.mdiMapMarkerRadius, module: 'Inventory & Storage' },
|
||||
{ key: 'suppliers', label: 'Suppliers', icon: icon.mdiTruckDelivery, module: 'Inventory & Storage' },
|
||||
|
||||
const organizationId = currentUser?.tenants?.id;
|
||||
{ key: 'invoices', label: 'Invoices', icon: icon.mdiInvoice, module: 'Accounting' },
|
||||
{ key: 'payments', label: 'Payments', icon: icon.mdiCreditCard, module: 'Accounting' },
|
||||
{ key: 'expenses', label: 'Expenses', icon: icon.mdiClipboardList, module: 'Accounting' },
|
||||
{ key: 'customers', label: 'Customers', icon: icon.mdiAccountCircle, module: 'Accounting' },
|
||||
|
||||
{ key: 'equipment_assets', label: 'Equipment Assets', icon: icon.mdiWrench, module: 'Assets & Orders' },
|
||||
{ key: 'orders', label: 'Orders', icon: icon.mdiTruck, module: 'Assets & Orders' },
|
||||
|
||||
{ key: 'users', label: 'Users', icon: icon.mdiAccountGroup, module: 'Administration' },
|
||||
{ key: 'subscriptions', label: 'Subscriptions', icon: icon.mdiCalendarClock, module: 'Administration' },
|
||||
{ key: 'activation_codes', label: 'Activation Codes', icon: icon.mdiKey, module: 'Administration' },
|
||||
];
|
||||
|
||||
async function loadData() {
|
||||
const entities = ['users','roles','permissions','tenants','user_roles','modules','tenant_modules','plans','subscriptions','activation_codes','licenses','audit_logs','products','stock_movements','warehouses','warehouse_locations','customers','suppliers','invoices','invoice_items','payments','expenses','equipment_assets','orders','order_items',];
|
||||
const fns = [setUsers,setRoles,setPermissions,setTenants,setUser_roles,setModules,setTenant_modules,setPlans,setSubscriptions,setActivation_codes,setLicenses,setAudit_logs,setProducts,setStock_movements,setWarehouses,setWarehouse_locations,setCustomers,setSuppliers,setInvoices,setInvoice_items,setPayments,setExpenses,setEquipment_assets,setOrders,setOrder_items,];
|
||||
|
||||
const requests = entities.map((entity, index) => {
|
||||
|
||||
if(hasPermission(currentUser, `READ_${entity.toUpperCase()}`)) {
|
||||
return axios.get(`/${entity.toLowerCase()}/count`);
|
||||
} else {
|
||||
fns[index](null);
|
||||
return Promise.resolve({data: {count: null}});
|
||||
const requests = entities.map((entity) => {
|
||||
if(hasPermission(currentUser, `READ_${entity.key.toUpperCase()}`)) {
|
||||
return axios.get(`/${entity.key.toLowerCase()}/count`);
|
||||
}
|
||||
|
||||
return Promise.resolve({ data: { count: null } });
|
||||
});
|
||||
|
||||
Promise.allSettled(requests).then((results) => {
|
||||
const results = await Promise.allSettled(requests);
|
||||
const newCounts: Record<string, any> = {};
|
||||
results.forEach((result, i) => {
|
||||
if (result.status === 'fulfilled') {
|
||||
fns[i](result.value.data.count);
|
||||
newCounts[entities[i].key] = (result.value as any).data.count;
|
||||
} else {
|
||||
fns[i](result.reason.message);
|
||||
newCounts[entities[i].key] = 'Error';
|
||||
}
|
||||
});
|
||||
});
|
||||
setCounts(newCounts);
|
||||
}
|
||||
|
||||
async function getWidgets(roleId) {
|
||||
await dispatch(fetchWidgets(roleId));
|
||||
}
|
||||
React.useEffect(() => {
|
||||
if (!currentUser) return;
|
||||
loadData().then();
|
||||
loadData();
|
||||
setWidgetsRole({ role: { value: currentUser?.app_role?.id, label: currentUser?.app_role?.name } });
|
||||
}, [currentUser]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!currentUser || !widgetsRole?.role?.value) return;
|
||||
getWidgets(widgetsRole?.role?.value || '').then();
|
||||
dispatch(fetchWidgets(widgetsRole?.role?.value));
|
||||
}, [widgetsRole?.role?.value]);
|
||||
|
||||
const renderModuleGroup = (moduleName: string) => {
|
||||
const moduleEntities = entities.filter(e => e.module === moduleName && hasPermission(currentUser, `READ_${e.key.toUpperCase()}`));
|
||||
if (moduleEntities.length === 0) return null;
|
||||
|
||||
return (
|
||||
<div key={moduleName} className="mb-10">
|
||||
<h3 className="text-xl font-bold mb-6 text-gray-700 dark:text-gray-300 flex items-center">
|
||||
<span className="w-1.5 h-6 bg-indigo-600 mr-3 rounded-full"></span>
|
||||
{moduleName}
|
||||
</h3>
|
||||
<div className="grid grid-cols-1 gap-6 lg:grid-cols-3 xl:grid-cols-4">
|
||||
{moduleEntities.map(e => (
|
||||
<EntityCard
|
||||
key={e.key}
|
||||
entity={e.key}
|
||||
label={e.label}
|
||||
count={counts[e.key] ?? loadingMessage}
|
||||
iconPath={e.icon}
|
||||
iconsColor={iconsColor}
|
||||
corners={corners}
|
||||
cardsStyle={cardsStyle}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>
|
||||
{getPageTitle('Overview')}
|
||||
</title>
|
||||
<title>{getPageTitle('Overview')}</title>
|
||||
</Head>
|
||||
<SectionMain>
|
||||
<SectionTitleLineWithButton
|
||||
icon={icon.mdiChartTimelineVariant}
|
||||
title='Overview'
|
||||
main>
|
||||
title='Enterprise Dashboard'
|
||||
main
|
||||
>
|
||||
{''}
|
||||
</SectionTitleLineWithButton>
|
||||
|
||||
{hasPermission(currentUser, 'CREATE_ROLES') && <WidgetCreator
|
||||
{/* Subscription Status Widget */}
|
||||
<div className={`mb-10 p-6 bg-indigo-50 border border-indigo-200 rounded-3xl flex flex-col md:flex-row items-center justify-between shadow-sm`}>
|
||||
<div className="flex items-center mb-4 md:mb-0">
|
||||
<div className="w-14 h-14 bg-indigo-100 text-indigo-600 rounded-2xl flex items-center justify-center mr-5 shadow-inner">
|
||||
<BaseIcon path={icon.mdiCheckDecagram} size={32} />
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="text-indigo-900 font-bold text-lg leading-tight">DIGIHELP-IMS Premium Active</h4>
|
||||
<p className="text-indigo-700 text-sm mt-0.5">Your enterprise subscription is valid until 2026-12-31</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex space-x-3">
|
||||
<BaseButton label="Renew" color="info" small className="rounded-xl px-6" />
|
||||
<BaseButton label="View Plan" outline color="info" small className="rounded-xl px-6" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{hasPermission(currentUser, 'CREATE_ROLES') && (
|
||||
<WidgetCreator
|
||||
currentUser={currentUser}
|
||||
isFetchingQuery={isFetchingQuery}
|
||||
setWidgetsRole={setWidgetsRole}
|
||||
widgetsRole={widgetsRole}
|
||||
/>}
|
||||
{!!rolesWidgets.length &&
|
||||
hasPermission(currentUser, 'CREATE_ROLES') && (
|
||||
<p className=' text-gray-500 dark:text-gray-400 mb-4'>
|
||||
{`${widgetsRole?.role?.label || 'Users'}'s widgets`}
|
||||
</p>
|
||||
/>
|
||||
)}
|
||||
|
||||
<div className='grid grid-cols-1 gap-6 lg:grid-cols-4 mb-6 grid-flow-dense'>
|
||||
{/* Dynamic AI Widgets */}
|
||||
{(rolesWidgets.length > 0 || isFetchingQuery || loading) && (
|
||||
<div className="mb-10">
|
||||
<h3 className="text-xl font-bold mb-6 text-gray-700 dark:text-gray-300 flex items-center">
|
||||
<span className="w-1.5 h-6 bg-violet-600 mr-3 rounded-full"></span>
|
||||
AI Insights & Reports
|
||||
</h3>
|
||||
<div className='grid grid-cols-1 gap-6 lg:grid-cols-4'>
|
||||
{(isFetchingQuery || loading) && (
|
||||
<div className={` ${corners !== 'rounded-full'? corners : 'rounded-3xl'} dark:bg-dark-900 text-lg leading-tight text-gray-500 flex items-center ${cardsStyle} dark:border-dark-700 p-6`}>
|
||||
<BaseIcon
|
||||
className={`${iconsColor} animate-spin mr-5`}
|
||||
w='w-16'
|
||||
h='h-16'
|
||||
size={48}
|
||||
path={icon.mdiLoading}
|
||||
/>{' '}
|
||||
Loading widgets...
|
||||
<div className={`${corners !== 'rounded-full'? corners : 'rounded-3xl'} dark:bg-dark-900 text-lg leading-tight text-gray-500 flex items-center ${cardsStyle} dark:border-dark-700 p-6 shadow-sm`}>
|
||||
<BaseIcon className={`${iconsColor} animate-spin mr-5`} w='w-12' h='h-12' size={32} path={icon.mdiLoading} />
|
||||
Generating Insights...
|
||||
</div>
|
||||
)}
|
||||
|
||||
{ rolesWidgets &&
|
||||
rolesWidgets.map((widget) => (
|
||||
{rolesWidgets.map((widget) => (
|
||||
<SmartWidget
|
||||
key={widget.id}
|
||||
userId={currentUser?.id}
|
||||
@ -156,714 +199,12 @@ const Dashboard = () => {
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!!rolesWidgets.length && <hr className='my-6 text-midnightBlueTheme-mainBG ' />}
|
||||
{/* Grouped Entity Cards */}
|
||||
{['Inventory & Storage', 'Accounting', 'Assets & Orders', 'Administration'].map(renderModuleGroup)}
|
||||
|
||||
<div id="dashboard" className='grid grid-cols-1 gap-6 lg:grid-cols-3 mb-6'>
|
||||
|
||||
|
||||
{hasPermission(currentUser, 'READ_USERS') && <Link href={'/users/users-list'}>
|
||||
<div
|
||||
className={`${corners !== 'rounded-full'? corners : 'rounded-3xl'} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
|
||||
>
|
||||
<div className="flex justify-between align-center">
|
||||
<div>
|
||||
<div className="text-lg leading-tight text-gray-500 dark:text-gray-400">
|
||||
Users
|
||||
</div>
|
||||
<div className="text-3xl leading-tight font-semibold">
|
||||
{users}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<BaseIcon
|
||||
className={`${iconsColor}`}
|
||||
w="w-16"
|
||||
h="h-16"
|
||||
size={48}
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
path={icon.mdiAccountGroup || icon.mdiTable}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>}
|
||||
|
||||
{hasPermission(currentUser, 'READ_ROLES') && <Link href={'/roles/roles-list'}>
|
||||
<div
|
||||
className={`${corners !== 'rounded-full'? corners : 'rounded-3xl'} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
|
||||
>
|
||||
<div className="flex justify-between align-center">
|
||||
<div>
|
||||
<div className="text-lg leading-tight text-gray-500 dark:text-gray-400">
|
||||
Roles
|
||||
</div>
|
||||
<div className="text-3xl leading-tight font-semibold">
|
||||
{roles}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<BaseIcon
|
||||
className={`${iconsColor}`}
|
||||
w="w-16"
|
||||
h="h-16"
|
||||
size={48}
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
path={icon.mdiShieldAccountVariantOutline || icon.mdiTable}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>}
|
||||
|
||||
{hasPermission(currentUser, 'READ_PERMISSIONS') && <Link href={'/permissions/permissions-list'}>
|
||||
<div
|
||||
className={`${corners !== 'rounded-full'? corners : 'rounded-3xl'} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
|
||||
>
|
||||
<div className="flex justify-between align-center">
|
||||
<div>
|
||||
<div className="text-lg leading-tight text-gray-500 dark:text-gray-400">
|
||||
Permissions
|
||||
</div>
|
||||
<div className="text-3xl leading-tight font-semibold">
|
||||
{permissions}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<BaseIcon
|
||||
className={`${iconsColor}`}
|
||||
w="w-16"
|
||||
h="h-16"
|
||||
size={48}
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
path={icon.mdiShieldAccountOutline || icon.mdiTable}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>}
|
||||
|
||||
{hasPermission(currentUser, 'READ_TENANTS') && <Link href={'/tenants/tenants-list'}>
|
||||
<div
|
||||
className={`${corners !== 'rounded-full'? corners : 'rounded-3xl'} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
|
||||
>
|
||||
<div className="flex justify-between align-center">
|
||||
<div>
|
||||
<div className="text-lg leading-tight text-gray-500 dark:text-gray-400">
|
||||
Tenants
|
||||
</div>
|
||||
<div className="text-3xl leading-tight font-semibold">
|
||||
{tenants}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<BaseIcon
|
||||
className={`${iconsColor}`}
|
||||
w="w-16"
|
||||
h="h-16"
|
||||
size={48}
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
path={icon.mdiTable || icon.mdiTable}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>}
|
||||
|
||||
{hasPermission(currentUser, 'READ_USER_ROLES') && <Link href={'/user_roles/user_roles-list'}>
|
||||
<div
|
||||
className={`${corners !== 'rounded-full'? corners : 'rounded-3xl'} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
|
||||
>
|
||||
<div className="flex justify-between align-center">
|
||||
<div>
|
||||
<div className="text-lg leading-tight text-gray-500 dark:text-gray-400">
|
||||
User roles
|
||||
</div>
|
||||
<div className="text-3xl leading-tight font-semibold">
|
||||
{user_roles}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<BaseIcon
|
||||
className={`${iconsColor}`}
|
||||
w="w-16"
|
||||
h="h-16"
|
||||
size={48}
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
path={'mdiAccountMultipleCheck' in icon ? icon['mdiAccountMultipleCheck' as keyof typeof icon] : icon.mdiTable || icon.mdiTable}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>}
|
||||
|
||||
{hasPermission(currentUser, 'READ_MODULES') && <Link href={'/modules/modules-list'}>
|
||||
<div
|
||||
className={`${corners !== 'rounded-full'? corners : 'rounded-3xl'} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
|
||||
>
|
||||
<div className="flex justify-between align-center">
|
||||
<div>
|
||||
<div className="text-lg leading-tight text-gray-500 dark:text-gray-400">
|
||||
Modules
|
||||
</div>
|
||||
<div className="text-3xl leading-tight font-semibold">
|
||||
{modules}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<BaseIcon
|
||||
className={`${iconsColor}`}
|
||||
w="w-16"
|
||||
h="h-16"
|
||||
size={48}
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
path={'mdiPuzzle' in icon ? icon['mdiPuzzle' as keyof typeof icon] : icon.mdiTable || icon.mdiTable}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>}
|
||||
|
||||
{hasPermission(currentUser, 'READ_TENANT_MODULES') && <Link href={'/tenant_modules/tenant_modules-list'}>
|
||||
<div
|
||||
className={`${corners !== 'rounded-full'? corners : 'rounded-3xl'} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
|
||||
>
|
||||
<div className="flex justify-between align-center">
|
||||
<div>
|
||||
<div className="text-lg leading-tight text-gray-500 dark:text-gray-400">
|
||||
Tenant modules
|
||||
</div>
|
||||
<div className="text-3xl leading-tight font-semibold">
|
||||
{tenant_modules}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<BaseIcon
|
||||
className={`${iconsColor}`}
|
||||
w="w-16"
|
||||
h="h-16"
|
||||
size={48}
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
path={'mdiToggleSwitch' in icon ? icon['mdiToggleSwitch' as keyof typeof icon] : icon.mdiTable || icon.mdiTable}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>}
|
||||
|
||||
{hasPermission(currentUser, 'READ_PLANS') && <Link href={'/plans/plans-list'}>
|
||||
<div
|
||||
className={`${corners !== 'rounded-full'? corners : 'rounded-3xl'} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
|
||||
>
|
||||
<div className="flex justify-between align-center">
|
||||
<div>
|
||||
<div className="text-lg leading-tight text-gray-500 dark:text-gray-400">
|
||||
Plans
|
||||
</div>
|
||||
<div className="text-3xl leading-tight font-semibold">
|
||||
{plans}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<BaseIcon
|
||||
className={`${iconsColor}`}
|
||||
w="w-16"
|
||||
h="h-16"
|
||||
size={48}
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
path={'mdiFinance' in icon ? icon['mdiFinance' as keyof typeof icon] : icon.mdiTable || icon.mdiTable}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>}
|
||||
|
||||
{hasPermission(currentUser, 'READ_SUBSCRIPTIONS') && <Link href={'/subscriptions/subscriptions-list'}>
|
||||
<div
|
||||
className={`${corners !== 'rounded-full'? corners : 'rounded-3xl'} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
|
||||
>
|
||||
<div className="flex justify-between align-center">
|
||||
<div>
|
||||
<div className="text-lg leading-tight text-gray-500 dark:text-gray-400">
|
||||
Subscriptions
|
||||
</div>
|
||||
<div className="text-3xl leading-tight font-semibold">
|
||||
{subscriptions}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<BaseIcon
|
||||
className={`${iconsColor}`}
|
||||
w="w-16"
|
||||
h="h-16"
|
||||
size={48}
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
path={'mdiCalendarClock' in icon ? icon['mdiCalendarClock' as keyof typeof icon] : icon.mdiTable || icon.mdiTable}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>}
|
||||
|
||||
{hasPermission(currentUser, 'READ_ACTIVATION_CODES') && <Link href={'/activation_codes/activation_codes-list'}>
|
||||
<div
|
||||
className={`${corners !== 'rounded-full'? corners : 'rounded-3xl'} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
|
||||
>
|
||||
<div className="flex justify-between align-center">
|
||||
<div>
|
||||
<div className="text-lg leading-tight text-gray-500 dark:text-gray-400">
|
||||
Activation codes
|
||||
</div>
|
||||
<div className="text-3xl leading-tight font-semibold">
|
||||
{activation_codes}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<BaseIcon
|
||||
className={`${iconsColor}`}
|
||||
w="w-16"
|
||||
h="h-16"
|
||||
size={48}
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
path={'mdiKey' in icon ? icon['mdiKey' as keyof typeof icon] : icon.mdiTable || icon.mdiTable}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>}
|
||||
|
||||
{hasPermission(currentUser, 'READ_LICENSES') && <Link href={'/licenses/licenses-list'}>
|
||||
<div
|
||||
className={`${corners !== 'rounded-full'? corners : 'rounded-3xl'} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
|
||||
>
|
||||
<div className="flex justify-between align-center">
|
||||
<div>
|
||||
<div className="text-lg leading-tight text-gray-500 dark:text-gray-400">
|
||||
Licenses
|
||||
</div>
|
||||
<div className="text-3xl leading-tight font-semibold">
|
||||
{licenses}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<BaseIcon
|
||||
className={`${iconsColor}`}
|
||||
w="w-16"
|
||||
h="h-16"
|
||||
size={48}
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
path={'mdiCertificate' in icon ? icon['mdiCertificate' as keyof typeof icon] : icon.mdiTable || icon.mdiTable}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>}
|
||||
|
||||
{hasPermission(currentUser, 'READ_AUDIT_LOGS') && <Link href={'/audit_logs/audit_logs-list'}>
|
||||
<div
|
||||
className={`${corners !== 'rounded-full'? corners : 'rounded-3xl'} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
|
||||
>
|
||||
<div className="flex justify-between align-center">
|
||||
<div>
|
||||
<div className="text-lg leading-tight text-gray-500 dark:text-gray-400">
|
||||
Audit logs
|
||||
</div>
|
||||
<div className="text-3xl leading-tight font-semibold">
|
||||
{audit_logs}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<BaseIcon
|
||||
className={`${iconsColor}`}
|
||||
w="w-16"
|
||||
h="h-16"
|
||||
size={48}
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
path={'mdiHistory' in icon ? icon['mdiHistory' as keyof typeof icon] : icon.mdiTable || icon.mdiTable}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>}
|
||||
|
||||
{hasPermission(currentUser, 'READ_PRODUCTS') && <Link href={'/products/products-list'}>
|
||||
<div
|
||||
className={`${corners !== 'rounded-full'? corners : 'rounded-3xl'} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
|
||||
>
|
||||
<div className="flex justify-between align-center">
|
||||
<div>
|
||||
<div className="text-lg leading-tight text-gray-500 dark:text-gray-400">
|
||||
Products
|
||||
</div>
|
||||
<div className="text-3xl leading-tight font-semibold">
|
||||
{products}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<BaseIcon
|
||||
className={`${iconsColor}`}
|
||||
w="w-16"
|
||||
h="h-16"
|
||||
size={48}
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
path={'mdiPackageVariant' in icon ? icon['mdiPackageVariant' as keyof typeof icon] : icon.mdiTable || icon.mdiTable}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>}
|
||||
|
||||
{hasPermission(currentUser, 'READ_STOCK_MOVEMENTS') && <Link href={'/stock_movements/stock_movements-list'}>
|
||||
<div
|
||||
className={`${corners !== 'rounded-full'? corners : 'rounded-3xl'} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
|
||||
>
|
||||
<div className="flex justify-between align-center">
|
||||
<div>
|
||||
<div className="text-lg leading-tight text-gray-500 dark:text-gray-400">
|
||||
Stock movements
|
||||
</div>
|
||||
<div className="text-3xl leading-tight font-semibold">
|
||||
{stock_movements}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<BaseIcon
|
||||
className={`${iconsColor}`}
|
||||
w="w-16"
|
||||
h="h-16"
|
||||
size={48}
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
path={'mdiSwapHorizontal' in icon ? icon['mdiSwapHorizontal' as keyof typeof icon] : icon.mdiTable || icon.mdiTable}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>}
|
||||
|
||||
{hasPermission(currentUser, 'READ_WAREHOUSES') && <Link href={'/warehouses/warehouses-list'}>
|
||||
<div
|
||||
className={`${corners !== 'rounded-full'? corners : 'rounded-3xl'} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
|
||||
>
|
||||
<div className="flex justify-between align-center">
|
||||
<div>
|
||||
<div className="text-lg leading-tight text-gray-500 dark:text-gray-400">
|
||||
Warehouses
|
||||
</div>
|
||||
<div className="text-3xl leading-tight font-semibold">
|
||||
{warehouses}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<BaseIcon
|
||||
className={`${iconsColor}`}
|
||||
w="w-16"
|
||||
h="h-16"
|
||||
size={48}
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
path={'mdiWarehouse' in icon ? icon['mdiWarehouse' as keyof typeof icon] : icon.mdiTable || icon.mdiTable}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>}
|
||||
|
||||
{hasPermission(currentUser, 'READ_WAREHOUSE_LOCATIONS') && <Link href={'/warehouse_locations/warehouse_locations-list'}>
|
||||
<div
|
||||
className={`${corners !== 'rounded-full'? corners : 'rounded-3xl'} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
|
||||
>
|
||||
<div className="flex justify-between align-center">
|
||||
<div>
|
||||
<div className="text-lg leading-tight text-gray-500 dark:text-gray-400">
|
||||
Warehouse locations
|
||||
</div>
|
||||
<div className="text-3xl leading-tight font-semibold">
|
||||
{warehouse_locations}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<BaseIcon
|
||||
className={`${iconsColor}`}
|
||||
w="w-16"
|
||||
h="h-16"
|
||||
size={48}
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
path={'mdiMapMarkerRadius' in icon ? icon['mdiMapMarkerRadius' as keyof typeof icon] : icon.mdiTable || icon.mdiTable}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>}
|
||||
|
||||
{hasPermission(currentUser, 'READ_CUSTOMERS') && <Link href={'/customers/customers-list'}>
|
||||
<div
|
||||
className={`${corners !== 'rounded-full'? corners : 'rounded-3xl'} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
|
||||
>
|
||||
<div className="flex justify-between align-center">
|
||||
<div>
|
||||
<div className="text-lg leading-tight text-gray-500 dark:text-gray-400">
|
||||
Customers
|
||||
</div>
|
||||
<div className="text-3xl leading-tight font-semibold">
|
||||
{customers}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<BaseIcon
|
||||
className={`${iconsColor}`}
|
||||
w="w-16"
|
||||
h="h-16"
|
||||
size={48}
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
path={'mdiAccountCircle' in icon ? icon['mdiAccountCircle' as keyof typeof icon] : icon.mdiTable || icon.mdiTable}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>}
|
||||
|
||||
{hasPermission(currentUser, 'READ_SUPPLIERS') && <Link href={'/suppliers/suppliers-list'}>
|
||||
<div
|
||||
className={`${corners !== 'rounded-full'? corners : 'rounded-3xl'} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
|
||||
>
|
||||
<div className="flex justify-between align-center">
|
||||
<div>
|
||||
<div className="text-lg leading-tight text-gray-500 dark:text-gray-400">
|
||||
Suppliers
|
||||
</div>
|
||||
<div className="text-3xl leading-tight font-semibold">
|
||||
{suppliers}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<BaseIcon
|
||||
className={`${iconsColor}`}
|
||||
w="w-16"
|
||||
h="h-16"
|
||||
size={48}
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
path={'mdiTruckDelivery' in icon ? icon['mdiTruckDelivery' as keyof typeof icon] : icon.mdiTable || icon.mdiTable}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>}
|
||||
|
||||
{hasPermission(currentUser, 'READ_INVOICES') && <Link href={'/invoices/invoices-list'}>
|
||||
<div
|
||||
className={`${corners !== 'rounded-full'? corners : 'rounded-3xl'} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
|
||||
>
|
||||
<div className="flex justify-between align-center">
|
||||
<div>
|
||||
<div className="text-lg leading-tight text-gray-500 dark:text-gray-400">
|
||||
Invoices
|
||||
</div>
|
||||
<div className="text-3xl leading-tight font-semibold">
|
||||
{invoices}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<BaseIcon
|
||||
className={`${iconsColor}`}
|
||||
w="w-16"
|
||||
h="h-16"
|
||||
size={48}
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
path={'mdiFileInvoice' in icon ? icon['mdiFileInvoice' as keyof typeof icon] : icon.mdiTable || icon.mdiTable}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>}
|
||||
|
||||
{hasPermission(currentUser, 'READ_INVOICE_ITEMS') && <Link href={'/invoice_items/invoice_items-list'}>
|
||||
<div
|
||||
className={`${corners !== 'rounded-full'? corners : 'rounded-3xl'} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
|
||||
>
|
||||
<div className="flex justify-between align-center">
|
||||
<div>
|
||||
<div className="text-lg leading-tight text-gray-500 dark:text-gray-400">
|
||||
Invoice items
|
||||
</div>
|
||||
<div className="text-3xl leading-tight font-semibold">
|
||||
{invoice_items}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<BaseIcon
|
||||
className={`${iconsColor}`}
|
||||
w="w-16"
|
||||
h="h-16"
|
||||
size={48}
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
path={'mdiReceipt' in icon ? icon['mdiReceipt' as keyof typeof icon] : icon.mdiTable || icon.mdiTable}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>}
|
||||
|
||||
{hasPermission(currentUser, 'READ_PAYMENTS') && <Link href={'/payments/payments-list'}>
|
||||
<div
|
||||
className={`${corners !== 'rounded-full'? corners : 'rounded-3xl'} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
|
||||
>
|
||||
<div className="flex justify-between align-center">
|
||||
<div>
|
||||
<div className="text-lg leading-tight text-gray-500 dark:text-gray-400">
|
||||
Payments
|
||||
</div>
|
||||
<div className="text-3xl leading-tight font-semibold">
|
||||
{payments}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<BaseIcon
|
||||
className={`${iconsColor}`}
|
||||
w="w-16"
|
||||
h="h-16"
|
||||
size={48}
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
path={'mdiCreditCard' in icon ? icon['mdiCreditCard' as keyof typeof icon] : icon.mdiTable || icon.mdiTable}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>}
|
||||
|
||||
{hasPermission(currentUser, 'READ_EXPENSES') && <Link href={'/expenses/expenses-list'}>
|
||||
<div
|
||||
className={`${corners !== 'rounded-full'? corners : 'rounded-3xl'} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
|
||||
>
|
||||
<div className="flex justify-between align-center">
|
||||
<div>
|
||||
<div className="text-lg leading-tight text-gray-500 dark:text-gray-400">
|
||||
Expenses
|
||||
</div>
|
||||
<div className="text-3xl leading-tight font-semibold">
|
||||
{expenses}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<BaseIcon
|
||||
className={`${iconsColor}`}
|
||||
w="w-16"
|
||||
h="h-16"
|
||||
size={48}
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
path={'mdiClipboardList' in icon ? icon['mdiClipboardList' as keyof typeof icon] : icon.mdiTable || icon.mdiTable}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>}
|
||||
|
||||
{hasPermission(currentUser, 'READ_EQUIPMENT_ASSETS') && <Link href={'/equipment_assets/equipment_assets-list'}>
|
||||
<div
|
||||
className={`${corners !== 'rounded-full'? corners : 'rounded-3xl'} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
|
||||
>
|
||||
<div className="flex justify-between align-center">
|
||||
<div>
|
||||
<div className="text-lg leading-tight text-gray-500 dark:text-gray-400">
|
||||
Equipment assets
|
||||
</div>
|
||||
<div className="text-3xl leading-tight font-semibold">
|
||||
{equipment_assets}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<BaseIcon
|
||||
className={`${iconsColor}`}
|
||||
w="w-16"
|
||||
h="h-16"
|
||||
size={48}
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
path={'mdiScrewdriverWrench' in icon ? icon['mdiScrewdriverWrench' as keyof typeof icon] : icon.mdiTable || icon.mdiTable}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>}
|
||||
|
||||
{hasPermission(currentUser, 'READ_ORDERS') && <Link href={'/orders/orders-list'}>
|
||||
<div
|
||||
className={`${corners !== 'rounded-full'? corners : 'rounded-3xl'} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
|
||||
>
|
||||
<div className="flex justify-between align-center">
|
||||
<div>
|
||||
<div className="text-lg leading-tight text-gray-500 dark:text-gray-400">
|
||||
Orders
|
||||
</div>
|
||||
<div className="text-3xl leading-tight font-semibold">
|
||||
{orders}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<BaseIcon
|
||||
className={`${iconsColor}`}
|
||||
w="w-16"
|
||||
h="h-16"
|
||||
size={48}
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
path={'mdiTruck' in icon ? icon['mdiTruck' as keyof typeof icon] : icon.mdiTable || icon.mdiTable}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>}
|
||||
|
||||
{hasPermission(currentUser, 'READ_ORDER_ITEMS') && <Link href={'/order_items/order_items-list'}>
|
||||
<div
|
||||
className={`${corners !== 'rounded-full'? corners : 'rounded-3xl'} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
|
||||
>
|
||||
<div className="flex justify-between align-center">
|
||||
<div>
|
||||
<div className="text-lg leading-tight text-gray-500 dark:text-gray-400">
|
||||
Order items
|
||||
</div>
|
||||
<div className="text-3xl leading-tight font-semibold">
|
||||
{order_items}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<BaseIcon
|
||||
className={`${iconsColor}`}
|
||||
w="w-16"
|
||||
h="h-16"
|
||||
size={48}
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
path={'mdiFormatListBulleted' in icon ? icon['mdiFormatListBulleted' as keyof typeof icon] : icon.mdiTable || icon.mdiTable}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>}
|
||||
|
||||
|
||||
</div>
|
||||
</SectionMain>
|
||||
</>
|
||||
)
|
||||
|
||||
@ -1,166 +1,192 @@
|
||||
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import React from 'react';
|
||||
import type { ReactElement } from 'react';
|
||||
import Head from 'next/head';
|
||||
import Link from 'next/link';
|
||||
import BaseButton from '../components/BaseButton';
|
||||
import CardBox from '../components/CardBox';
|
||||
import SectionFullScreen from '../components/SectionFullScreen';
|
||||
import LayoutGuest from '../layouts/Guest';
|
||||
import BaseDivider from '../components/BaseDivider';
|
||||
import BaseButtons from '../components/BaseButtons';
|
||||
import { getPageTitle } from '../config';
|
||||
import { useAppSelector } from '../stores/hooks';
|
||||
import CardBoxComponentTitle from "../components/CardBoxComponentTitle";
|
||||
import { getPexelsImage, getPexelsVideo } from '../helpers/pexels';
|
||||
import { mdiPackageVariant, mdiWarehouse, mdiInvoice, mdiTruck, mdiWrench } from '@mdi/js';
|
||||
import BaseIcon from '../components/BaseIcon';
|
||||
|
||||
const features = [
|
||||
{
|
||||
title: 'Inventory Management',
|
||||
description: 'Track stock levels, products, and movements in real-time.',
|
||||
icon: mdiPackageVariant,
|
||||
},
|
||||
{
|
||||
title: 'Storage & Warehouses',
|
||||
description: 'Manage multiple warehouses and locations with ease.',
|
||||
icon: mdiWarehouse,
|
||||
},
|
||||
{
|
||||
title: 'Accounting & Invoices',
|
||||
description: 'Complete billing system with payments and expense tracking.',
|
||||
icon: mdiInvoice,
|
||||
},
|
||||
{
|
||||
title: 'Shipping & Orders',
|
||||
description: 'Handle customer orders and shipping workflows efficiently.',
|
||||
icon: mdiTruck,
|
||||
},
|
||||
{
|
||||
title: 'Asset Management',
|
||||
description: 'Track equipment and assets across your organization.',
|
||||
icon: mdiWrench,
|
||||
},
|
||||
];
|
||||
|
||||
export default function Starter() {
|
||||
const [illustrationImage, setIllustrationImage] = useState({
|
||||
src: undefined,
|
||||
photographer: undefined,
|
||||
photographer_url: undefined,
|
||||
})
|
||||
const [illustrationVideo, setIllustrationVideo] = useState({video_files: []})
|
||||
const [contentType, setContentType] = useState('image');
|
||||
const [contentPosition, setContentPosition] = useState('right');
|
||||
const textColor = useAppSelector((state) => state.style.linkColor);
|
||||
|
||||
const title = 'DIGIHELP-IMS'
|
||||
|
||||
// Fetch Pexels image/video
|
||||
useEffect(() => {
|
||||
async function fetchData() {
|
||||
const image = await getPexelsImage();
|
||||
const video = await getPexelsVideo();
|
||||
setIllustrationImage(image);
|
||||
setIllustrationVideo(video);
|
||||
}
|
||||
fetchData();
|
||||
}, []);
|
||||
|
||||
const imageBlock = (image) => (
|
||||
<div
|
||||
className='hidden md:flex flex-col justify-end relative flex-grow-0 flex-shrink-0 w-1/3'
|
||||
style={{
|
||||
backgroundImage: `${
|
||||
image
|
||||
? `url(${image?.src?.original})`
|
||||
: 'linear-gradient(rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0.5))'
|
||||
}`,
|
||||
backgroundSize: 'cover',
|
||||
backgroundPosition: 'left center',
|
||||
backgroundRepeat: 'no-repeat',
|
||||
}}
|
||||
>
|
||||
<div className='flex justify-center w-full bg-blue-300/20'>
|
||||
<a
|
||||
className='text-[8px]'
|
||||
href={image?.photographer_url}
|
||||
target='_blank'
|
||||
rel='noreferrer'
|
||||
>
|
||||
Photo by {image?.photographer} on Pexels
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const videoBlock = (video) => {
|
||||
if (video?.video_files?.length > 0) {
|
||||
export default function LandingPage() {
|
||||
return (
|
||||
<div className='hidden md:flex flex-col justify-end relative flex-grow-0 flex-shrink-0 w-1/3'>
|
||||
<video
|
||||
className='absolute top-0 left-0 w-full h-full object-cover'
|
||||
autoPlay
|
||||
loop
|
||||
muted
|
||||
>
|
||||
<source src={video?.video_files[0]?.link} type='video/mp4'/>
|
||||
Your browser does not support the video tag.
|
||||
</video>
|
||||
<div className='flex justify-center w-full bg-blue-300/20 z-10'>
|
||||
<a
|
||||
className='text-[8px]'
|
||||
href={video?.user?.url}
|
||||
target='_blank'
|
||||
rel='noreferrer'
|
||||
>
|
||||
Video by {video.user.name} on Pexels
|
||||
</a>
|
||||
</div>
|
||||
</div>)
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
style={
|
||||
contentPosition === 'background'
|
||||
? {
|
||||
backgroundImage: `${
|
||||
illustrationImage
|
||||
? `url(${illustrationImage.src?.original})`
|
||||
: 'linear-gradient(rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0.5))'
|
||||
}`,
|
||||
backgroundSize: 'cover',
|
||||
backgroundPosition: 'left center',
|
||||
backgroundRepeat: 'no-repeat',
|
||||
}
|
||||
: {}
|
||||
}
|
||||
>
|
||||
<div className="bg-white text-gray-900 font-sans">
|
||||
<Head>
|
||||
<title>{getPageTitle('Starter Page')}</title>
|
||||
<title>{getPageTitle('DIGIHELP-IMS - Integrated Management Suite')}</title>
|
||||
<meta name="description" content="Next-generation SaaS for Inventory, Storage, Accounting, and Enterprise Management." />
|
||||
</Head>
|
||||
|
||||
<SectionFullScreen bg='violet'>
|
||||
<div
|
||||
className={`flex ${
|
||||
contentPosition === 'right' ? 'flex-row-reverse' : 'flex-row'
|
||||
} min-h-screen w-full`}
|
||||
>
|
||||
{contentType === 'image' && contentPosition !== 'background'
|
||||
? imageBlock(illustrationImage)
|
||||
: null}
|
||||
{contentType === 'video' && contentPosition !== 'background'
|
||||
? videoBlock(illustrationVideo)
|
||||
: null}
|
||||
<div className='flex items-center justify-center flex-col space-y-4 w-full lg:w-full'>
|
||||
<CardBox className='w-full md:w-3/5 lg:w-2/3'>
|
||||
<CardBoxComponentTitle title="Welcome to your DIGIHELP-IMS app!"/>
|
||||
|
||||
<div className="space-y-3">
|
||||
<p className='text-center '>This is a React.js/Node.js app generated by the <a className={`${textColor}`} href="https://flatlogic.com/generator">Flatlogic Web App Generator</a></p>
|
||||
<p className='text-center '>For guides and documentation please check
|
||||
your local README.md and the <a className={`${textColor}`} href="https://flatlogic.com/documentation">Flatlogic documentation</a></p>
|
||||
{/* Hero Section */}
|
||||
<section className="relative py-20 overflow-hidden bg-gradient-to-br from-indigo-900 via-violet-800 to-indigo-900 text-white">
|
||||
<div className="absolute top-0 left-0 w-full h-full opacity-10">
|
||||
<div className="absolute top-10 left-10 w-64 h-64 bg-white rounded-full blur-3xl"></div>
|
||||
<div className="absolute bottom-10 right-10 w-96 h-96 bg-indigo-300 rounded-full blur-3xl"></div>
|
||||
</div>
|
||||
|
||||
<BaseButtons>
|
||||
<div className="container mx-auto px-6 relative z-10 text-center">
|
||||
<h1 className="text-5xl md:text-7xl font-extrabold mb-6 tracking-tight animate-fade-in">
|
||||
DIGIHELP-IMS
|
||||
</h1>
|
||||
<p className="text-xl md:text-2xl mb-10 text-indigo-100 max-w-2xl mx-auto leading-relaxed">
|
||||
The Integrated Management Suite for Modern Enterprise.
|
||||
Streamline Inventory, Accounting, and Logistics in one unified platform.
|
||||
</p>
|
||||
<div className="flex flex-col md:flex-row justify-center space-y-4 md:space-y-0 md:space-x-6">
|
||||
<BaseButton
|
||||
href='/login'
|
||||
label='Login'
|
||||
color='info'
|
||||
className='w-full'
|
||||
href="/register"
|
||||
label="Start Free Trial"
|
||||
color="white"
|
||||
className="px-10 py-4 text-lg font-bold rounded-xl shadow-2xl hover:scale-105 transition-all duration-300"
|
||||
/>
|
||||
<BaseButton
|
||||
href="/login"
|
||||
label="Sign In"
|
||||
outline
|
||||
color="white"
|
||||
className="px-10 py-4 text-lg font-bold rounded-xl border-2 hover:bg-white/10 transition-all duration-300"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</BaseButtons>
|
||||
</CardBox>
|
||||
</div>
|
||||
</div>
|
||||
</SectionFullScreen>
|
||||
<div className='bg-black text-white flex flex-col text-center justify-center md:flex-row'>
|
||||
<p className='py-6 text-sm'>© 2026 <span>{title}</span>. All rights reserved</p>
|
||||
<Link className='py-6 ml-4 text-sm' href='/privacy-policy/'>
|
||||
Privacy Policy
|
||||
</Link>
|
||||
{/* Features Section */}
|
||||
<section className="py-24 bg-gray-50">
|
||||
<div className="container mx-auto px-6">
|
||||
<div className="text-center mb-20">
|
||||
<h2 className="text-3xl md:text-4xl font-bold mb-4 text-gray-900">Comprehensive Enterprise Modules</h2>
|
||||
<div className="w-20 h-1 bg-indigo-600 mx-auto mb-6"></div>
|
||||
<p className="text-gray-600 max-w-2xl mx-auto text-lg">
|
||||
Everything you need to run your business operations smoothly and efficiently.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-10">
|
||||
{features.map((feature, index) => (
|
||||
<div key={index} className="bg-white p-10 rounded-3xl shadow-sm border border-gray-100 hover:shadow-2xl hover:-translate-y-2 transition-all duration-300 group">
|
||||
<div className="w-16 h-16 bg-indigo-50 text-indigo-600 rounded-2xl flex items-center justify-center mb-8 group-hover:bg-indigo-600 group-hover:text-white transition-colors duration-300">
|
||||
<BaseIcon path={feature.icon} size={36} />
|
||||
</div>
|
||||
<h3 className="text-2xl font-bold mb-4">{feature.title}</h3>
|
||||
<p className="text-gray-600 leading-relaxed text-lg">{feature.description}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* How It Works Section */}
|
||||
<section className="py-24 bg-indigo-900 text-white">
|
||||
<div className="container mx-auto px-6">
|
||||
<div className="text-center mb-16">
|
||||
<h2 className="text-3xl md:text-4xl font-bold mb-4">How It Works</h2>
|
||||
<p className="text-indigo-200">Scale your business in three simple steps.</p>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-12 text-center">
|
||||
{[
|
||||
{ step: '01', title: 'Register Tenant', desc: 'Create your organization account and subdomain.' },
|
||||
{ step: '02', title: 'Activate Modules', desc: 'Choose the modules your business needs to grow.' },
|
||||
{ step: '03', title: 'Start Scaling', desc: 'Onboard your team and automate your workflows.' },
|
||||
].map((item, idx) => (
|
||||
<div key={idx} className="relative">
|
||||
<div className="text-6xl font-black text-white/10 absolute -top-10 left-1/2 -translate-x-1/2 z-0">{item.step}</div>
|
||||
<h3 className="text-2xl font-bold mb-4 relative z-10">{item.title}</h3>
|
||||
<p className="text-indigo-200 relative z-10">{item.desc}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Pricing Section */}
|
||||
<section className="py-24 bg-white">
|
||||
<div className="container mx-auto px-6">
|
||||
<div className="text-center mb-16">
|
||||
<h2 className="text-3xl md:text-4xl font-bold mb-4 text-gray-900 text-center">Flexible Subscription Plans</h2>
|
||||
<p className="text-gray-600">Choose a plan that fits your business scale.</p>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8">
|
||||
{['Weekly', 'Monthly', 'Yearly', 'Lifetime'].map((plan) => (
|
||||
<div key={plan} className={`p-8 rounded-3xl border ${plan === 'Yearly' ? 'border-indigo-600 bg-indigo-50 ring-4 ring-indigo-600 ring-opacity-10' : 'border-gray-200'} hover:shadow-xl transition-all duration-300`}>
|
||||
<h3 className="text-lg font-semibold text-gray-500 mb-2 uppercase tracking-widest">{plan}</h3>
|
||||
<div className="text-4xl font-bold mb-6 text-indigo-600">
|
||||
{plan === 'Lifetime' ? 'Contact' : '$XX'}
|
||||
</div>
|
||||
<ul className="mb-8 space-y-3 text-gray-600 text-sm">
|
||||
<li>✓ All Modules Included</li>
|
||||
<li>✓ Unlimited Users</li>
|
||||
<li>✓ Priority Support</li>
|
||||
<li>✓ 99.9% Uptime SLA</li>
|
||||
</ul>
|
||||
<BaseButton label="Get Started" color={plan === 'Yearly' ? 'info' : 'white'} outline={plan !== 'Yearly'} className="w-full rounded-xl py-3 font-bold" href="/register" />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Footer */}
|
||||
<footer className="bg-gray-900 text-white py-16 border-t border-gray-800">
|
||||
<div className="container mx-auto px-6">
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-12 mb-12">
|
||||
<div>
|
||||
<h3 className="text-2xl font-bold mb-6 text-indigo-400">DIGIHELP-IMS</h3>
|
||||
<p className="text-gray-400 leading-relaxed">
|
||||
Next-generation enterprise resource planning and management suite.
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="font-bold mb-6 uppercase tracking-wider text-gray-500 text-sm">Product</h4>
|
||||
<ul className="space-y-4 text-gray-400">
|
||||
<li><Link href="/login" className="hover:text-white transition-colors">Dashboard</Link></li>
|
||||
<li><Link href="/register" className="hover:text-white transition-colors">Pricing</Link></li>
|
||||
<li><Link href="/modules" className="hover:text-white transition-colors">Modules</Link></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="font-bold mb-6 uppercase tracking-wider text-gray-500 text-sm">Legal</h4>
|
||||
<ul className="space-y-4 text-gray-400">
|
||||
<li><Link href="/privacy-policy" className="hover:text-white transition-colors">Privacy Policy</Link></li>
|
||||
<li><Link href="/terms-of-use" className="hover:text-white transition-colors">Terms of Service</Link></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div className="pt-12 border-t border-gray-800 text-center text-gray-500 text-sm">
|
||||
<p>© 2026 DIGIHELP-IMS. All rights reserved.</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Starter.getLayout = function getLayout(page: ReactElement) {
|
||||
LandingPage.getLayout = function getLayout(page: ReactElement) {
|
||||
return <LayoutGuest>{page}</LayoutGuest>;
|
||||
};
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user