Merge pull request 'furqan' (#15) from furqan into dev
Reviewed-on: https://gitea.maskantech.in/gitea_admin/saas_frontend/pulls/15
This commit is contained in:
-3
@@ -21,7 +21,6 @@ export default function SignUpForm() {
|
||||
setIsLoading(true);
|
||||
|
||||
if (!isChecked) {
|
||||
// Require terms acceptance before sending the signup request.
|
||||
setErrorMessage("Please accept the terms and conditions to continue.");
|
||||
setIsLoading(false);
|
||||
return;
|
||||
@@ -35,10 +34,8 @@ export default function SignUpForm() {
|
||||
};
|
||||
|
||||
try {
|
||||
// Call the backend signup API.
|
||||
await authApi.signup(payload);
|
||||
|
||||
// Signup does not return tokens, so take the user to signin.
|
||||
navigate("/signin");
|
||||
} catch (error) {
|
||||
const message =
|
||||
@@ -3,9 +3,11 @@ import axios from 'axios';
|
||||
import { ExternalLink, Box, Search, Rocket } from 'lucide-react';
|
||||
import { moduleApi, type Module } from '../modules/user/UserModuleApi';
|
||||
import { useAuth } from '../../context/AuthContext';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const Dashboard = () => {
|
||||
const { user } = useAuth();
|
||||
const { t } = useTranslation(['dashboard', 'common']);
|
||||
|
||||
const [modules, setModules] = useState<Module[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
@@ -54,7 +56,7 @@ const Dashboard = () => {
|
||||
if (filtered.length === 0) return {};
|
||||
|
||||
filtered.forEach(module => {
|
||||
const category = module.category || 'All Modules';
|
||||
const category = module.category || t('dashboard:allModules');
|
||||
if (!groups[category]) {
|
||||
groups[category] = [];
|
||||
}
|
||||
@@ -68,7 +70,7 @@ const Dashboard = () => {
|
||||
},
|
||||
{} as Record<string, Module[]>
|
||||
);
|
||||
}, [modules, searchTerm]);
|
||||
}, [modules, searchTerm, t]);
|
||||
|
||||
return (
|
||||
<div className="p-8 space-y-10 min-h-screen bg-gray-50/50">
|
||||
@@ -76,10 +78,10 @@ const Dashboard = () => {
|
||||
<div className="flex flex-col md:flex-row justify-between items-start md:items-center gap-6 animate-fade-in-up">
|
||||
<div>
|
||||
<h1 className="text-4xl font-extrabold text-(--text-primary) tracking-tight mb-2">
|
||||
Welcome back, {user?.first_name || 'Admin'}! 👋
|
||||
{t('dashboard:title')}, {user?.first_name || 'Admin'}! 👋
|
||||
</h1>
|
||||
<p className="text-lg text-(--text-secondary)">
|
||||
Manage your SaaS ecosystem from one central hub.
|
||||
{t('dashboard:subtitle')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -90,7 +92,7 @@ const Dashboard = () => {
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search available modules..."
|
||||
placeholder={t('dashboard:searchPlaceholder')}
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
className="block w-full pl-10 pr-3 py-2.5 bg-white border border-gray-200 rounded-xl leading-5 text-gray-900 placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-blue-500/20 focus:border-blue-500 transition-all shadow-sm hover:shadow-md"
|
||||
@@ -160,7 +162,7 @@ const Dashboard = () => {
|
||||
className="w-full flex items-center justify-center gap-2 py-2.5 px-4 bg-gray-50 hover:bg-blue-600 text-gray-700 hover:text-white rounded-xl font-medium transition-all duration-200 group-hover:shadow-md"
|
||||
>
|
||||
<Rocket size={18} className="transition-transform group-hover:-translate-y-0.5 group-hover:translate-x-0.5" />
|
||||
Launch Module
|
||||
{t('dashboard:actions.launch')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -173,16 +175,16 @@ const Dashboard = () => {
|
||||
<div className="w-16 h-16 bg-gray-50 rounded-full flex items-center justify-center mb-4">
|
||||
<Box className="w-8 h-8 text-gray-400" />
|
||||
</div>
|
||||
<h3 className="text-lg font-semibold text-gray-900 mb-1">No modules found</h3>
|
||||
<h3 className="text-lg font-semibold text-gray-900 mb-1">{t('common:common.noData')}</h3>
|
||||
<p className="text-gray-500 max-w-sm mx-auto">
|
||||
{searchTerm ? `No results found for "${searchTerm}". Try a different keyword.` : "Your dashboard is currently empty. Contact your administrator to assign modules."}
|
||||
{searchTerm ? t('common:common.noData') : "Your dashboard is currently empty. Contact your administrator to assign modules."}
|
||||
</p>
|
||||
{searchTerm && (
|
||||
<button
|
||||
onClick={() => setSearchTerm('')}
|
||||
className="mt-4 text-blue-600 font-medium hover:underline"
|
||||
>
|
||||
Clear Search
|
||||
{t('common:actions.reset')}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -10,7 +10,7 @@ import CustomButton from '../../../../components/custom/CustomButton';
|
||||
|
||||
const ModuleList = () => {
|
||||
const navigate = useNavigate();
|
||||
const { t } = useTranslation('modules');
|
||||
const { t } = useTranslation(['modules', 'common']);
|
||||
const { listModules, deleteModule, loading } = useModuleApi();
|
||||
const [modules, setModules] = useState<Module[]>([]);
|
||||
const [showForm, setShowForm] = useState(false);
|
||||
@@ -125,14 +125,14 @@ const ModuleList = () => {
|
||||
className="flex-1 flex items-center justify-center gap-2 px-3 py-2 bg-gray-100 hover:bg-gray-200 rounded-lg transition-colors text-sm text-(--text-primary)"
|
||||
>
|
||||
<Settings size={16} />
|
||||
Environments
|
||||
{t('registry.card.environments')}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => navigate(`/admin/modules/${module.id}/permissions`)}
|
||||
className="flex-1 flex items-center justify-center gap-2 px-3 py-2 bg-gray-100 hover:bg-gray-200 rounded-lg transition-colors text-sm text-(--text-primary)"
|
||||
>
|
||||
<Shield size={16} />
|
||||
Permissions
|
||||
{t('registry.card.permissions')}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -142,14 +142,14 @@ const ModuleList = () => {
|
||||
className="flex-1 flex items-center justify-center gap-2 px-3 py-2 bg-blue-50 hover:bg-blue-100 text-blue-600 rounded-lg transition-colors text-sm"
|
||||
>
|
||||
<Edit size={16} />
|
||||
Edit
|
||||
{t('common:actions.edit')}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => confirmDelete(module)}
|
||||
className="flex-1 flex items-center justify-center gap-2 px-3 py-2 bg-red-50 hover:bg-red-100 text-red-600 rounded-lg transition-colors text-sm"
|
||||
>
|
||||
<Trash2 size={16} />
|
||||
Delete
|
||||
{t('common:actions.delete')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -9,8 +9,10 @@ import { paletteApi } from "../PaletteApi";
|
||||
import type { ColorPalette } from "../ThemeTypes";
|
||||
import PaletteForm from "./PaletteForm";
|
||||
import { useTheme } from "../../../context/ThemeContext";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const AllPalettes: React.FC = () => {
|
||||
const { t } = useTranslation(['theme', 'common']);
|
||||
const [palettes, setPalettes] = useState<ColorPalette[]>([]);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [errorMessage, setErrorMessage] = useState("");
|
||||
@@ -25,7 +27,7 @@ const AllPalettes: React.FC = () => {
|
||||
const data = await paletteApi.getAllPalettes();
|
||||
setPalettes(data);
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : "Failed to fetch palettes";
|
||||
const message = error instanceof Error ? error.message : t('theme:errors.fetchFailed');
|
||||
setErrorMessage(message);
|
||||
console.error("Failed to fetch palettes", error);
|
||||
} finally {
|
||||
@@ -48,10 +50,10 @@ const AllPalettes: React.FC = () => {
|
||||
};
|
||||
|
||||
const handleDelete = async (id: string) => {
|
||||
if (!window.confirm("Are you sure you want to delete this palette?")) return;
|
||||
if (!window.confirm(t('theme:confirmDelete'))) return;
|
||||
try {
|
||||
await paletteApi.deletePalette(id);
|
||||
toast.success("Palette deleted successfully");
|
||||
toast.success(t('theme:success.deleted'));
|
||||
fetchPalettes();
|
||||
} catch (error) {
|
||||
// toast handled by api client
|
||||
@@ -63,10 +65,10 @@ const AllPalettes: React.FC = () => {
|
||||
try {
|
||||
if (editingPalette) {
|
||||
await paletteApi.updatePalette(editingPalette.id, data);
|
||||
toast.success("Palette updated successfully");
|
||||
toast.success(t('theme:success.updated'));
|
||||
} else {
|
||||
await paletteApi.createPalette(data);
|
||||
toast.success("Palette created successfully");
|
||||
toast.success(t('theme:success.created'));
|
||||
}
|
||||
setIsModalOpen(false);
|
||||
fetchPalettes();
|
||||
@@ -81,7 +83,7 @@ const AllPalettes: React.FC = () => {
|
||||
() => [
|
||||
{
|
||||
key: "name",
|
||||
header: "Name",
|
||||
header: t('theme:table.name'),
|
||||
searchable: true,
|
||||
render: (row) => (
|
||||
<div>
|
||||
@@ -94,43 +96,43 @@ const AllPalettes: React.FC = () => {
|
||||
},
|
||||
{
|
||||
key: "is_default",
|
||||
header: "Default",
|
||||
header: t('theme:table.default'),
|
||||
render: (row) => (
|
||||
row.is_default ? (
|
||||
<CustomStatus status="Default" variant="success" />
|
||||
<CustomStatus status={t('common:status.default')} variant="success" />
|
||||
) : null
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "colors",
|
||||
header: "Preview",
|
||||
header: t('theme:table.preview'),
|
||||
render: (row) => (
|
||||
<div className="flex items-center gap-1">
|
||||
<div
|
||||
className="w-6 h-6 rounded-full border border-(--card-border) shadow-sm"
|
||||
style={{ backgroundColor: row.colors.primary }}
|
||||
title="Primary"
|
||||
title={t('theme:colors.primary')}
|
||||
/>
|
||||
<div
|
||||
className="w-6 h-6 rounded-full border border-(--card-border) shadow-sm"
|
||||
style={{ backgroundColor: row.colors.sidebar_bg }}
|
||||
title="Sidebar"
|
||||
title={t('theme:colors.sidebar')}
|
||||
/>
|
||||
<div
|
||||
className="w-6 h-6 rounded-full border border-(--card-border) shadow-sm"
|
||||
style={{ backgroundColor: row.colors.header_bg }}
|
||||
title="Header"
|
||||
title={t('theme:colors.header')}
|
||||
/>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "actions",
|
||||
header: "Actions",
|
||||
header: t('common:columns.actions'),
|
||||
render: (row) => (
|
||||
<CustomActionMenu>
|
||||
<CustomActionItem onClick={() => handleEdit(row)}>
|
||||
<Edit2 size={16} className="mr-2" /> Edit Palette
|
||||
<Edit2 size={16} className="mr-2" /> {t('theme:actions.edit')}
|
||||
</CustomActionItem>
|
||||
|
||||
<CustomActionItem
|
||||
@@ -146,7 +148,7 @@ const AllPalettes: React.FC = () => {
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Trash2 size={16} className="mr-2" /> Delete Palette
|
||||
<Trash2 size={16} className="mr-2" /> {t('theme:actions.delete')}
|
||||
</div>
|
||||
</CustomActionItem>
|
||||
</CustomActionMenu>
|
||||
@@ -160,13 +162,13 @@ const AllPalettes: React.FC = () => {
|
||||
<div className="space-y-6">
|
||||
<div className="flex flex-wrap items-center justify-between gap-4">
|
||||
<div>
|
||||
<h1 className="text-2xl font-semibold text-(--text-primary)">Theme Palettes</h1>
|
||||
<h1 className="text-2xl font-semibold text-(--text-primary)">{t('theme:title')}</h1>
|
||||
{/* <p className="text-sm text-(--text-secondary) mt-1">
|
||||
Manage system color themes and default palettes.
|
||||
</p> */}
|
||||
</div>
|
||||
<CustomButton onClick={handleCreate} leftIcon={<Plus size={18} />}>
|
||||
Create Palette
|
||||
{t('theme:actions.create')}
|
||||
</CustomButton>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React, { useEffect } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { Check } from "lucide-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import CustomModal from "../../../components/custom/CustomModal";
|
||||
import CustomInput from "../../../components/custom/CustomInput";
|
||||
import {CustomButton} from "../../../components/custom";
|
||||
@@ -15,27 +16,27 @@ interface PaletteFormProps {
|
||||
}
|
||||
|
||||
const colorFields = [
|
||||
{ label: "Primary Color", key: "primary" },
|
||||
{ label: "Primary Hover", key: "primary_hover" },
|
||||
{ label: "Sidebar Background", key: "sidebar_bg" },
|
||||
{ label: "Sidebar Text", key: "sidebar_text" },
|
||||
{ label: "Sidebar Text Muted", key: "sidebar_text_muted" },
|
||||
{ label: "Sidebar Active BG", key: "sidebar_active_bg" },
|
||||
{ label: "Sidebar Active Text", key: "sidebar_active_text" },
|
||||
{ label: "Sidebar Hover BG", key: "sidebar_hover_bg" },
|
||||
{ label: "Sidebar Border", key: "sidebar_border" },
|
||||
{ label: "Header Background", key: "header_bg" },
|
||||
{ label: "Header Text", key: "header_text" },
|
||||
{ label: "Header Border", key: "header_border" },
|
||||
{ label: "App Background", key: "background" },
|
||||
{ label: "App Secondary BG", key: "background_secondary" },
|
||||
{ label: "Text Primary", key: "text_primary" },
|
||||
{ label: "Text Secondary", key: "text_secondary" },
|
||||
{ label: "Card Background", key: "card_bg" },
|
||||
{ label: "Card Border", key: "card_border" },
|
||||
{ label: "Table Header BG", key: "table_header_bg" },
|
||||
{ label: "Table Row Hover", key: "table_row_hover" },
|
||||
{ label: "Table Border", key: "table_border" },
|
||||
{ key: "primary" },
|
||||
{ key: "primary_hover" },
|
||||
{ key: "sidebar_bg" },
|
||||
{ key: "sidebar_text" },
|
||||
{ key: "sidebar_text_muted" },
|
||||
{ key: "sidebar_active_bg" },
|
||||
{ key: "sidebar_active_text" },
|
||||
{ key: "sidebar_hover_bg" },
|
||||
{ key: "sidebar_border" },
|
||||
{ key: "header_bg" },
|
||||
{ key: "header_text" },
|
||||
{ key: "header_border" },
|
||||
{ key: "background" },
|
||||
{ key: "background_secondary" },
|
||||
{ key: "text_primary" },
|
||||
{ key: "text_secondary" },
|
||||
{ key: "card_bg" },
|
||||
{ key: "card_border" },
|
||||
{ key: "table_header_bg" },
|
||||
{ key: "table_row_hover" },
|
||||
{ key: "table_border" },
|
||||
];
|
||||
|
||||
const emptyColors = colorFields.reduce((acc, field) => {
|
||||
@@ -50,6 +51,7 @@ const PaletteForm: React.FC<PaletteFormProps> = ({
|
||||
isLoading,
|
||||
isOpen,
|
||||
}) => {
|
||||
const { t } = useTranslation(['theme', 'common']);
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
@@ -96,7 +98,7 @@ const PaletteForm: React.FC<PaletteFormProps> = ({
|
||||
onClick={onCancel}
|
||||
disabled={isLoading}
|
||||
>
|
||||
Cancel
|
||||
{t('theme:actions.cancel')}
|
||||
</CustomButton>
|
||||
<CustomButton
|
||||
type="submit"
|
||||
@@ -105,7 +107,7 @@ const PaletteForm: React.FC<PaletteFormProps> = ({
|
||||
loading={isLoading}
|
||||
leftIcon={<Check size={16} />}
|
||||
>
|
||||
{initialData ? "Update Palette" : "Create Palette"}
|
||||
{initialData ? t('theme:actions.update') : t('theme:actions.create')}
|
||||
</CustomButton>
|
||||
</div>
|
||||
);
|
||||
@@ -114,7 +116,7 @@ const PaletteForm: React.FC<PaletteFormProps> = ({
|
||||
<CustomModal
|
||||
isOpen={isOpen}
|
||||
onClose={onCancel}
|
||||
title={initialData ? "Edit Palette" : "Create New Palette"}
|
||||
title={initialData ? t('theme:form.title.edit') : t('theme:form.title.create')}
|
||||
size="lg"
|
||||
footer={footer}
|
||||
>
|
||||
@@ -126,15 +128,15 @@ const PaletteForm: React.FC<PaletteFormProps> = ({
|
||||
>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<CustomInput
|
||||
label="Palette Name"
|
||||
{...register("name", { required: "Name is required" })}
|
||||
label={t('theme:form.fields.name.label')}
|
||||
{...register("name", { required: t('theme:form.fields.name.required') })}
|
||||
error={errors.name?.message as string}
|
||||
placeholder="e.g. Dark Mode"
|
||||
placeholder={t('theme:form.fields.name.placeholder')}
|
||||
/>
|
||||
<CustomInput
|
||||
label="Description"
|
||||
label={t('theme:form.fields.description.label')}
|
||||
{...register("description")}
|
||||
placeholder="Optional description"
|
||||
placeholder={t('theme:form.fields.description.placeholder')}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -146,13 +148,13 @@ const PaletteForm: React.FC<PaletteFormProps> = ({
|
||||
className="h-4 w-4 rounded border-gray-300 text-blue-600 focus:ring-blue-500"
|
||||
/>
|
||||
<label htmlFor="is_default" className="text-sm font-medium text-gray-700">
|
||||
Set as Default Palette
|
||||
{t('theme:form.fields.isDefault')}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div className="border-t border-gray-200 pt-4">
|
||||
<h3 className="text-lg font-medium text-gray-900 mb-4">
|
||||
Color Configuration
|
||||
{t('theme:form.fields.sectionTitle')}
|
||||
</h3>
|
||||
|
||||
<div className="max-h-[60vh] overflow-y-auto pr-2 grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
@@ -175,9 +177,9 @@ const PaletteForm: React.FC<PaletteFormProps> = ({
|
||||
/>
|
||||
|
||||
<CustomInput
|
||||
label={field.label}
|
||||
label={t(`theme:colors.${field.key}`)}
|
||||
{...register(`colors.${field.key}` as any, {
|
||||
required: "Required",
|
||||
required: t('theme:form.fields.required'),
|
||||
})}
|
||||
placeholder="#000000"
|
||||
containerClassName="flex-1 mb-0"
|
||||
|
||||
@@ -123,7 +123,7 @@ const AppHeader: React.FC = () => {
|
||||
</div>
|
||||
|
||||
{/* RIGHT: Actions */}
|
||||
<div className="flex items-center gap-2 sm:gap-4 ml-auto">
|
||||
<div className="flex items-center gap-2 sm:gap-4 ms-auto">
|
||||
|
||||
{/* User Menu */}
|
||||
<div className="relative" ref={userMenuRef}>
|
||||
@@ -144,7 +144,7 @@ const AppHeader: React.FC = () => {
|
||||
{initials}
|
||||
</div>
|
||||
|
||||
<div className="hidden md:block text-left mr-1">
|
||||
<div className="hidden md:block text-left me-1">
|
||||
<p className="text-sm font-medium text-(--header-text) leading-none">{fullName}</p>
|
||||
</div>
|
||||
|
||||
@@ -159,7 +159,7 @@ const AppHeader: React.FC = () => {
|
||||
{isUserMenuOpen && (
|
||||
<div
|
||||
className="
|
||||
absolute right-0 mt-2 w-56
|
||||
absolute end-0 mt-2 w-56
|
||||
rounded-xl border border-(--card-border)
|
||||
bg-(--card-bg)
|
||||
shadow-md
|
||||
|
||||
@@ -83,7 +83,7 @@ const navItems: NavItem[] = [
|
||||
];
|
||||
|
||||
const AppSidebar: React.FC = () => {
|
||||
const { t } = useTranslation('common');
|
||||
const { t } = useTranslation(['common', 'modules', 'dashboard']);
|
||||
const navigate = useNavigate();
|
||||
const {
|
||||
isExpanded,
|
||||
@@ -114,12 +114,17 @@ const AppSidebar: React.FC = () => {
|
||||
};
|
||||
|
||||
const key = keyMap[name];
|
||||
if (!key) {
|
||||
console.warn(`Missing translation key for: ${name}`);
|
||||
return name;
|
||||
if (key) {
|
||||
return t(`nav.${key}`, name);
|
||||
}
|
||||
|
||||
return t(`nav.${key}`, name);
|
||||
|
||||
// Try other namespaces if not in nav map
|
||||
return t([
|
||||
`modules:${name}`,
|
||||
`dashboard:${name}`,
|
||||
`common:nav.${name}`,
|
||||
name
|
||||
]);
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -15,6 +15,9 @@ import arDashboard from './locales/ar/dashboard.json';
|
||||
import enModules from './locales/en/modules.json';
|
||||
import arModules from './locales/ar/modules.json';
|
||||
|
||||
import enTheme from './locales/en/theme.json';
|
||||
import arTheme from './locales/ar/theme.json';
|
||||
|
||||
export const languages = {
|
||||
en: { name: 'English', dir: 'ltr' },
|
||||
ar: { name: 'العربية', dir: 'rtl' },
|
||||
@@ -30,6 +33,7 @@ const resources = {
|
||||
profile: enProfile,
|
||||
dashboard: enDashboard,
|
||||
modules: enModules,
|
||||
theme: enTheme,
|
||||
},
|
||||
ar: {
|
||||
common: arCommon,
|
||||
@@ -38,6 +42,7 @@ const resources = {
|
||||
profile: arProfile,
|
||||
dashboard: arDashboard,
|
||||
modules: arModules,
|
||||
theme: arTheme,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -11,7 +11,8 @@
|
||||
"profile": "الملف الشخصي",
|
||||
"settings": "الإعدادات",
|
||||
"configuration": "التكوين",
|
||||
"dropdowns": "القوائم المنسدلة"
|
||||
"dropdowns": "القوائم المنسدلة",
|
||||
"modules": "الوحدات"
|
||||
},
|
||||
"actions": {
|
||||
"save": "حفظ",
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
{
|
||||
"title": "لوحة المعلومات قيد التطوير",
|
||||
"message": "نحن نعمل على إنشاء تجربة لوحة معلومات قوية لمنحك رؤى أفضل. يرجى التحقق مرة أخرى قريبا!"
|
||||
"title": "مرحباً بعودتك",
|
||||
"subtitle": "أدِر نظام SaaS الخاص بك من مركز واحد.",
|
||||
"allModules": "جميع الوحدات",
|
||||
"searchPlaceholder": "البحث في الوحدات المتاحة...",
|
||||
"actions": {
|
||||
"launch": "تشغيل الوحدة"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
{
|
||||
"registry": {
|
||||
"title": "Module Registry",
|
||||
"subtitle": "Manage platform modules and their configurations",
|
||||
"create_button": "Create Module",
|
||||
"title": "سجل الوحدات",
|
||||
"subtitle": "إدارة وحدات المنصة وتكويناتها",
|
||||
"create_button": "إنشاء وحدة",
|
||||
"empty_state": {
|
||||
"title": "No Modules Yet",
|
||||
"subtitle": "Create your first module to get started"
|
||||
"title": "لا توجد وحدات بعد",
|
||||
"subtitle": "قم بإنشاء وحدتك الأولى للبدء"
|
||||
},
|
||||
"card": {
|
||||
"environments": "البيئات",
|
||||
"permissions": "الأذونات"
|
||||
}
|
||||
},
|
||||
"form": {
|
||||
@@ -55,9 +59,14 @@
|
||||
}
|
||||
},
|
||||
"delete_modal": {
|
||||
"title": "Delete Module",
|
||||
"description": "Are you sure you want to delete \"{{name}}\"? This action cannot be undone and will remove all associated environments and tenant assignments.",
|
||||
"confirm": "Delete Module",
|
||||
"cancel": "Cancel"
|
||||
}
|
||||
"title": "حذف الوحدة",
|
||||
"description": "هل أنت متأكد أنك تريد حذف \"{{name}}\"؟ لا يمكن التراجع عن هذا الإجراء وسيتم إزالة جميع البيئات وتعيينات المستأجرين المرتبطة.",
|
||||
"confirm": "حذف الوحدة",
|
||||
"cancel": "إلغاء"
|
||||
},
|
||||
"Inventory": "المخزون",
|
||||
"Fulfillment & Logistics": "التنفيذ والخدمات اللوجستية",
|
||||
"User Management": "إدارة المستخدمين",
|
||||
"Tenant Management": "إدارة المستأجرين",
|
||||
"Role Management": "إدارة الأدوار"
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
{
|
||||
"title": "أنظمة الألوان",
|
||||
"errors": {
|
||||
"fetchFailed": "فشل جلب الأنظمة"
|
||||
},
|
||||
"confirmDelete": "هل أنت متأكد أنك تريد حذف هذا النظام؟",
|
||||
"success": {
|
||||
"deleted": "تم حذف النظام بنجاح",
|
||||
"updated": "تم تحديث النظام بنجاح",
|
||||
"created": "تم إنشاء النظام بنجاح"
|
||||
},
|
||||
"table": {
|
||||
"name": "الاسم",
|
||||
"default": "الافتراضي",
|
||||
"preview": "المعاينة"
|
||||
},
|
||||
"colors": {
|
||||
"primary": "اللون الأساسي",
|
||||
"primary_hover": "تأثير مرور الماوس الأساسي",
|
||||
"sidebar_bg": "خلفية الشريط الجانبي",
|
||||
"sidebar_text": "نص الشريط الجانبي",
|
||||
"sidebar_text_muted": "نص الشريط الجانبي (باهت)",
|
||||
"sidebar_active_bg": "خلفية الشريط الجانبي (نشط)",
|
||||
"sidebar_active_text": "نص الشريط الجانبي (نشط)",
|
||||
"sidebar_hover_bg": "خلفية مرور الماوس للشريط الجانبي",
|
||||
"sidebar_border": "حدود الشريط الجانبي",
|
||||
"header_bg": "خلفية الرأس",
|
||||
"header_text": "نص الرأس",
|
||||
"header_border": "حدود الرأس",
|
||||
"background": "خلفية التطبيق",
|
||||
"background_secondary": "خلفية ثانوية للتطبيق",
|
||||
"text_primary": "النص الأساسي",
|
||||
"text_secondary": "النص الثانوي",
|
||||
"card_bg": "خلفية البطاقة",
|
||||
"card_border": "حدود البطاقة",
|
||||
"table_header_bg": "خلفية رأس الجدول",
|
||||
"table_row_hover": "تأثير مرور الوموس للجدول",
|
||||
"table_border": "حدود الجدول"
|
||||
},
|
||||
"actions": {
|
||||
"edit": "تعديل النظام",
|
||||
"delete": "حذف النظام",
|
||||
"create": "إنشاء نظام جديد",
|
||||
"update": "تحديث النظام",
|
||||
"cancel": "إلغاء"
|
||||
},
|
||||
"form": {
|
||||
"title": {
|
||||
"create": "إنشاء نظام جديد",
|
||||
"edit": "تعديل النظام"
|
||||
},
|
||||
"fields": {
|
||||
"name": {
|
||||
"label": "اسم النظام",
|
||||
"placeholder": "مثال: الوضع الداكن",
|
||||
"required": "الاسم مطلوب"
|
||||
},
|
||||
"description": {
|
||||
"label": "الوصف",
|
||||
"placeholder": "وصف اختياري"
|
||||
},
|
||||
"isDefault": "تعيين كنظام افتراضي",
|
||||
"sectionTitle": "تكوين الألوان",
|
||||
"required": "مطلوب"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,9 @@
|
||||
{
|
||||
"title": "Dashboard"
|
||||
"title": "Welcome back",
|
||||
"subtitle": "Manage your SaaS ecosystem from one central hub.",
|
||||
"allModules": "All Modules",
|
||||
"searchPlaceholder": "Search available modules...",
|
||||
"actions": {
|
||||
"launch": "Launch Module"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,10 @@
|
||||
"empty_state": {
|
||||
"title": "No Modules Yet",
|
||||
"subtitle": "Create your first module to get started"
|
||||
},
|
||||
"card": {
|
||||
"environments": "Environments",
|
||||
"permissions": "Permissions"
|
||||
}
|
||||
},
|
||||
"form": {
|
||||
@@ -59,5 +63,10 @@
|
||||
"description": "Are you sure you want to delete \"{{name}}\"? This action cannot be undone and will remove all associated environments and tenant assignments.",
|
||||
"confirm": "Delete Module",
|
||||
"cancel": "Cancel"
|
||||
}
|
||||
},
|
||||
"Inventory": "Inventory",
|
||||
"Fulfillment & Logistics": "Fulfillment & Logistics",
|
||||
"User Management": "User Management",
|
||||
"Tenant Management": "Tenant Management",
|
||||
"Role Management": "Role Management"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
{
|
||||
"title": "Theme Palettes",
|
||||
"errors": {
|
||||
"fetchFailed": "Failed to fetch palettes"
|
||||
},
|
||||
"confirmDelete": "Are you sure you want to delete this palette?",
|
||||
"success": {
|
||||
"deleted": "Palette deleted successfully",
|
||||
"updated": "Palette updated successfully",
|
||||
"created": "Palette created successfully"
|
||||
},
|
||||
"table": {
|
||||
"name": "Name",
|
||||
"default": "Default",
|
||||
"preview": "Preview"
|
||||
},
|
||||
"colors": {
|
||||
"primary": "Primary Color",
|
||||
"primary_hover": "Primary Hover",
|
||||
"sidebar_bg": "Sidebar Background",
|
||||
"sidebar_text": "Sidebar Text",
|
||||
"sidebar_text_muted": "Sidebar Text Muted",
|
||||
"sidebar_active_bg": "Sidebar Active BG",
|
||||
"sidebar_active_text": "Sidebar Active Text",
|
||||
"sidebar_hover_bg": "Sidebar Hover BG",
|
||||
"sidebar_border": "Sidebar Border",
|
||||
"header_bg": "Header Background",
|
||||
"header_text": "Header Text",
|
||||
"header_border": "Header Border",
|
||||
"background": "App Background",
|
||||
"background_secondary": "App Secondary BG",
|
||||
"text_primary": "Text Primary",
|
||||
"text_secondary": "Text Secondary",
|
||||
"card_bg": "Card Background",
|
||||
"card_border": "Card Border",
|
||||
"table_header_bg": "Table Header BG",
|
||||
"table_row_hover": "Table Row Hover",
|
||||
"table_border": "Table Border"
|
||||
},
|
||||
"actions": {
|
||||
"edit": "Edit Palette",
|
||||
"delete": "Delete Palette",
|
||||
"create": "Create Palette",
|
||||
"update": "Update Palette",
|
||||
"cancel": "Cancel"
|
||||
},
|
||||
"form": {
|
||||
"title": {
|
||||
"create": "Create New Palette",
|
||||
"edit": "Edit Palette"
|
||||
},
|
||||
"fields": {
|
||||
"name": {
|
||||
"label": "Palette Name",
|
||||
"placeholder": "e.g. Dark Mode",
|
||||
"required": "Name is required"
|
||||
},
|
||||
"description": {
|
||||
"label": "Description",
|
||||
"placeholder": "Optional description"
|
||||
},
|
||||
"isDefault": "Set as Default Palette",
|
||||
"sectionTitle": "Color Configuration",
|
||||
"required": "Required"
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user