Autosave: 20260407-151714

This commit is contained in:
Flatlogic Bot 2026-04-07 15:17:14 +00:00
parent e34c261c25
commit 818e7d5818
3 changed files with 341 additions and 0 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

View File

@ -135,6 +135,11 @@ const menuAside: MenuAsideItem[] = [
icon: 'mdiFolderMultipleImage' in icon ? icon['mdiFolderMultipleImage' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
permissions: 'READ_ASSETS'
},
{
href: '/mai-server',
label: 'MAi Server',
icon: icon.mdiServer,
},
{
href: '/profile',
label: 'Profile',

View File

@ -0,0 +1,336 @@
import * as icon from '@mdi/js';
import Head from 'next/head';
import React from 'react';
import axios from 'axios';
import type { ReactElement } from 'react';
import BaseButton from '../components/BaseButton';
import BaseIcon from '../components/BaseIcon';
import CardBox from '../components/CardBox';
import FormField from '../components/FormField';
import SectionMain from '../components/SectionMain';
import SectionTitleLineWithButton from '../components/SectionTitleLineWithButton';
import { getPageTitle } from '../config';
import { hasPermission } from '../helpers/userPermissions';
import LayoutAuthenticated from '../layouts/Authenticated';
import { useAppSelector } from '../stores/hooks';
type MetricState = Record<'profiles' | 'conversations' | 'messages' | 'announcements', string>;
type MetricConfig = {
key: keyof MetricState;
title: string;
detail: string;
endpoint: string;
permission: string;
iconPath: string;
};
const moduleLabels = [
'Ai',
'AS Ai',
'MAI',
'MAI SERVER ALIYO MOMOT',
'D MOTHERBOARD',
'D RAM',
'D ROM',
'D WLAN',
'ALIYO MOMOT BANK ALIYO MOMOT',
];
const readinessItems = [
{ label: 'Register', description: 'Account onboarding is available through the existing sign up flow.' },
{ label: 'Authentification', description: 'Session access can be enabled directly from this control page.' },
{ label: 'Profile', description: 'Profile data stays synced with the authenticated application user.' },
{ label: 'Password', description: 'Password reset and recovery reuse the built-in auth endpoints.' },
{ label: 'Country', description: 'Regional profile details can be completed from the profile screen.' },
];
const adminItems = [
{ label: 'Admin', description: 'Administrator-level visibility is preserved through the role model.' },
{ label: 'Profile database', description: 'Profile records are available through the existing user management module.' },
{ label: 'iSecurity', description: 'Email verification and JWT session protection stay active for secure access.' },
{ label: 'iManager', description: 'Operational shortcuts stay routed through dashboard, profile, and entity pages.' },
{ label: 'iAuthentifier', description: 'Authentication status is surfaced clearly for the current session.' },
];
const metricConfigs: MetricConfig[] = [
{
key: 'profiles',
title: 'Profiles',
detail: 'Registered profile records',
endpoint: '/users/count',
permission: 'READ_USERS',
iconPath: icon.mdiAccountGroup,
},
{
key: 'conversations',
title: 'Marketing conversations',
detail: 'Lead and outreach threads',
endpoint: '/conversations/count',
permission: 'READ_CONVERSATIONS',
iconPath: icon.mdiChartTimelineVariant,
},
{
key: 'messages',
title: 'Message flow',
detail: 'Communication volume',
endpoint: '/messages/count',
permission: 'READ_MESSAGES',
iconPath: icon.mdiMessageTextOutline,
},
{
key: 'announcements',
title: 'Marketing broadcasts',
detail: 'Announcement and campaign records',
endpoint: '/announcements/count',
permission: 'READ_ANNOUNCEMENTS',
iconPath: icon.mdiBullhornOutline,
},
];
const MaiServerPage = () => {
const { currentUser } = useAppSelector((state) => state.auth);
const iconsColor = useAppSelector((state) => state.style.iconsColor);
const corners = useAppSelector((state) => state.style.corners);
const cardsStyle = useAppSelector((state) => state.style.cardsStyle);
const [isAuthenticationEnabled, setIsAuthenticationEnabled] = React.useState(false);
const [metrics, setMetrics] = React.useState<MetricState>({
profiles: '—',
conversations: '—',
messages: '—',
announcements: '—',
});
React.useEffect(() => {
if (!currentUser) return;
const loadMetrics = async () => {
const requests = metricConfigs.map(async (metric) => {
if (!hasPermission(currentUser, metric.permission)) {
return { key: metric.key, value: 'Private' };
}
try {
const response = await axios.get(metric.endpoint);
return { key: metric.key, value: String(response.data?.count ?? 0) };
} catch (error) {
console.error(`Failed to load ${metric.key} metric`, error);
return { key: metric.key, value: 'Unavailable' };
}
});
const results = await Promise.all(requests);
setMetrics((previous) => {
const next = { ...previous };
results.forEach((result) => {
next[result.key] = result.value;
});
return next;
});
};
loadMetrics();
}, [currentUser]);
const fullName = [currentUser?.firstName, currentUser?.lastName].filter(Boolean).join(' ');
const displayName = fullName || currentUser?.email || 'Authenticated user';
const emailAddress = currentUser?.email || 'No email connected';
const provider = currentUser?.provider || 'local';
const roleName = currentUser?.app_role?.name || 'Workspace member';
const countryValueRaw = currentUser?.country || currentUser?.organization?.country;
const countryName = typeof countryValueRaw === 'string' && countryValueRaw ? countryValueRaw : 'Not configured';
const emailVerificationStatus = currentUser?.emailVerified ? 'Verified' : 'Pending';
const authenticationStatus = isAuthenticationEnabled ? 'Enabled for this session' : 'Standby';
const authenticationTone = isAuthenticationEnabled ? 'text-green-600 dark:text-green-400' : 'text-amber-600 dark:text-amber-400';
return (
<>
<Head>
<title>{getPageTitle('Aliyo Momot MAi Server')}</title>
</Head>
<SectionMain>
<SectionTitleLineWithButton icon={icon.mdiServer} title="Aliyo Momot MAi Server" main>
{''}
</SectionTitleLineWithButton>
<CardBox className="mb-6 overflow-hidden">
<div className="flex flex-col gap-5 lg:flex-row lg:items-center lg:justify-between">
<div>
<p className="mb-2 text-xs font-semibold uppercase tracking-[0.3em] text-gray-500 dark:text-gray-400">
MAi server Aliyo Momot 1 and 2 · Server login
</p>
<h2 className="text-2xl font-semibold text-gray-900 dark:text-white">
Branded authentication, profiles, email access, and marketing visibility in one place.
</h2>
<p className="mt-3 max-w-3xl text-sm text-gray-600 dark:text-gray-300">
This separate in-app dashboard keeps the existing login flow intact while giving you a dedicated
Aliyo Momot MAi Server experience for server entry readiness, profile context, email visibility, and
high-level marketing statistics.
</p>
</div>
<div className="flex flex-col gap-3 sm:flex-row lg:flex-col xl:flex-row">
<BaseButton
color={isAuthenticationEnabled ? 'success' : 'contrast'}
icon={isAuthenticationEnabled ? icon.mdiCheckCircle : icon.mdiShieldCheck}
label={isAuthenticationEnabled ? 'Authentication Enabled' : 'Enable Authentication'}
onClick={() => setIsAuthenticationEnabled((previous) => !previous)}
/>
<BaseButton color="lightDark" icon={icon.mdiAccountCircle} label="Open Profile" href="/profile" />
</div>
</div>
<div className="mt-5 flex flex-wrap gap-2">
{moduleLabels.map((label) => (
<div
key={label}
className={`${corners !== 'rounded-full' ? corners : 'rounded-3xl'} border border-gray-200 bg-white/70 px-3 py-2 text-xs font-semibold uppercase tracking-wide text-gray-600 dark:border-dark-700 dark:bg-dark-800/80 dark:text-gray-300`}
>
{label}
</div>
))}
</div>
</CardBox>
<div className="mb-6 grid grid-cols-1 gap-6 md:grid-cols-2 xl:grid-cols-4">
{metricConfigs.map((metric) => (
<CardBox key={metric.key}>
<div className="flex items-start justify-between gap-4">
<div>
<p className="text-sm font-semibold text-gray-500 dark:text-gray-400">{metric.title}</p>
<p className="mt-2 text-3xl font-bold text-gray-900 dark:text-white">{metrics[metric.key]}</p>
<p className="mt-2 text-sm text-gray-500 dark:text-gray-400">{metric.detail}</p>
</div>
<BaseIcon
className={`${iconsColor}`}
path={metric.iconPath}
size={42}
w="w-12"
h="h-12"
/>
</div>
</CardBox>
))}
</div>
<div className="grid grid-cols-1 gap-6 xl:grid-cols-3">
<CardBox>
<div className="mb-4 flex items-center gap-3">
<BaseIcon className={`${iconsColor}`} path={icon.mdiAccountCircle} size={30} />
<div>
<h3 className="text-xl font-semibold text-gray-900 dark:text-white">Register & profile</h3>
<p className="text-sm text-gray-500 dark:text-gray-400">Profile, password, and regional setup blocks</p>
</div>
</div>
<div className="space-y-3">
{readinessItems.map((item) => (
<div key={item.label} className="flex items-start gap-3 border-b border-gray-100 pb-3 last:border-b-0 dark:border-dark-700">
<BaseIcon path={icon.mdiCheckCircle} className="mt-0.5 text-green-600 dark:text-green-400" size={18} />
<div>
<p className="font-semibold text-gray-900 dark:text-white">{item.label}</p>
<p className="text-sm text-gray-500 dark:text-gray-400">{item.description}</p>
</div>
</div>
))}
</div>
<div className={`mt-4 ${corners !== 'rounded-full' ? corners : 'rounded-3xl'} ${cardsStyle} border border-dashed border-gray-300 p-4 dark:border-dark-700`}>
<p className="text-xs font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400">Current user</p>
<p className="mt-2 text-lg font-semibold text-gray-900 dark:text-white">{displayName}</p>
<p className="text-sm text-gray-500 dark:text-gray-400">{roleName}</p>
<p className="mt-3 text-sm text-gray-500 dark:text-gray-400">Country / region: {countryName}</p>
</div>
</CardBox>
<CardBox>
<div className="mb-4 flex items-center gap-3">
<BaseIcon className={`${iconsColor}`} path={icon.mdiDatabase} size={30} />
<div>
<h3 className="text-xl font-semibold text-gray-900 dark:text-white">Admin profile database</h3>
<p className="text-sm text-gray-500 dark:text-gray-400">Administrative controls inspired by the shared mockup</p>
</div>
</div>
<div className="space-y-3">
{adminItems.map((item) => (
<div key={item.label} className="flex items-start gap-3 border-b border-gray-100 pb-3 last:border-b-0 dark:border-dark-700">
<BaseIcon path={icon.mdiCog} className="mt-0.5 text-blue-600 dark:text-blue-400" size={18} />
<div>
<p className="font-semibold text-gray-900 dark:text-white">{item.label}</p>
<p className="text-sm text-gray-500 dark:text-gray-400">{item.description}</p>
</div>
</div>
))}
</div>
<div className="mt-4 grid grid-cols-1 gap-3 sm:grid-cols-2">
<div className={`${corners !== 'rounded-full' ? corners : 'rounded-3xl'} border border-gray-200 p-4 dark:border-dark-700`}>
<p className="text-xs font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400">Server entry database</p>
<p className="mt-2 text-lg font-semibold text-gray-900 dark:text-white">Protected</p>
<p className="text-sm text-gray-500 dark:text-gray-400">Access remains tied to your authenticated app session.</p>
</div>
<div className={`${corners !== 'rounded-full' ? corners : 'rounded-3xl'} border border-gray-200 p-4 dark:border-dark-700`}>
<p className="text-xs font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400">Email database</p>
<p className="mt-2 text-lg font-semibold text-gray-900 dark:text-white">{emailVerificationStatus}</p>
<p className="text-sm text-gray-500 dark:text-gray-400">Primary email readiness for outreach and verification.</p>
</div>
</div>
</CardBox>
<CardBox>
<div className="mb-4 flex items-center gap-3">
<BaseIcon className={`${iconsColor}`} path={icon.mdiLanConnect} size={30} />
<div>
<h3 className="text-xl font-semibold text-gray-900 dark:text-white">Server login</h3>
<p className="text-sm text-gray-500 dark:text-gray-400">Email and authentication details for the current operator</p>
</div>
</div>
<FormField label="Email">
<input value={emailAddress} readOnly />
</FormField>
<FormField label="Password">
<input type="password" value="••••••••••••" readOnly />
</FormField>
<FormField label="Authentication status">
<input value={authenticationStatus} readOnly />
</FormField>
<FormField label="Provider">
<input value={provider} readOnly />
</FormField>
<div className={`mb-4 ${corners !== 'rounded-full' ? corners : 'rounded-3xl'} border px-4 py-3 dark:border-dark-700`}>
<div className="flex items-center justify-between gap-3">
<div>
<p className="text-xs font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400">Live status</p>
<p className={`mt-1 text-sm font-semibold ${authenticationTone}`}>{authenticationStatus}</p>
</div>
<BaseIcon
path={isAuthenticationEnabled ? icon.mdiCheckCircle : icon.mdiAlertCircle}
className={authenticationTone}
size={22}
/>
</div>
</div>
<div className="flex flex-wrap gap-3">
<BaseButton color="contrast" icon={icon.mdiShieldCheck} label="Enable Authentication" onClick={() => setIsAuthenticationEnabled(true)} />
<BaseButton color="lightDark" icon={icon.mdiEmailOutline} label="Profile Email" href="/profile" />
<BaseButton color="whiteDark" icon={icon.mdiOpenInNew} label="Forgot Password" href="/forgot" />
</div>
</CardBox>
</div>
</SectionMain>
</>
);
};
MaiServerPage.getLayout = function getLayout(page: ReactElement) {
return <LayoutAuthenticated>{page}</LayoutAuthenticated>;
};
export default MaiServerPage;