Merge pull request 'ameenah' (#18) from ameenah into dev

Reviewed-on: https://gitea.maskantech.in/gitea_admin/saas_frontend/pulls/18
This commit is contained in:
furqan
2026-04-03 09:38:38 +00:00
5 changed files with 165 additions and 138 deletions
@@ -1,7 +1,7 @@
import { useState, useEffect } from 'react';
import { X } from 'lucide-react';
import { adminModuleApi } from '../AdminModuleApi';
import type { ModuleEnvironment, EnvironmentCreate, EnvironmentUpdate } from '../AdminModuleTypes';
import CustomModal from '../../../../components/custom/CustomModal';
interface EnvironmentFormProps {
moduleId: string;
@@ -90,63 +90,89 @@ const EnvironmentForm = ({ moduleId, environment, onClose }: EnvironmentFormProp
};
return (
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4">
<div className="bg-white rounded-lg shadow-xl max-w-3xl w-full max-h-[90vh] overflow-y-auto">
<div className="sticky top-0 bg-white border-b px-6 py-4 flex items-center justify-between">
<h2 className="text-xl font-semibold">{environment ? 'Edit' : 'Create'} Environment</h2>
<button onClick={() => onClose(false)} className="p-2 hover:bg-gray-100 rounded-lg">
<X size={20} />
<CustomModal
isOpen={true}
onClose={() => onClose(false)}
title={environment ? "Edit Environment" : "Create Environment"}
size="lg"
footer={
<div className="flex gap-3 w-full">
<button
type="submit"
form="env-form"
disabled={loading}
className="flex-1 px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 disabled:opacity-50 transition-colors"
>
{loading ? "Saving..." : environment ? "Update" : "Create"}
</button>
<button
type="button"
onClick={() => onClose(false)}
className="flex-1 px-4 py-2 bg-[#334155] text-white rounded-lg hover:bg-[#1e293b] transition-colors"
>
Cancel
</button>
</div>
<form onSubmit={handleSubmit} className="p-6 space-y-4">
{error && <div className="bg-red-50 border border-red-200 text-red-700 px-4 py-3 rounded-lg">{error}</div>}
}
>
<form id="env-form" onSubmit={handleSubmit} className="space-y-4">
{error && (
<div className="bg-red-50 border border-red-200 text-red-700 px-4 py-3 rounded-lg">
{error}
</div>
)}
<div className="grid grid-cols-2 gap-4">
<div>
<label className="block text-sm font-medium mb-1">Environment Slug *</label>
<label className="block text-sm font-medium mb-1 text-(--text-primary)">
Environment Slug *
</label>
<input
type="text"
value={formData.slug}
onChange={(e) => setFormData({ ...formData, slug: e.target.value })}
className="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
className="w-full px-3 py-2 bg-(--background) border border-(--card-border) text-(--text-primary) rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
placeholder="prod, staging, eu-prod"
required
disabled={!!environment}
/>
</div>
<div>
<label className="block text-sm font-medium mb-1">Trust Type *</label>
<select
value={formData.trust_type}
onChange={(e) => setFormData({ ...formData, trust_type: e.target.value })}
className="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
>
<option value="hmac">HMAC-SHA256</option>
<option value="static_key">Static Key</option>
</select>
<label className="block text-sm font-medium mb-1 text-(--text-primary)">
Trust Type
</label>
<input
type="text"
value="HMAC-SHA256"
disabled
className="w-full px-3 py-2 bg-(--background) border border-(--card-border) text-(--text-secondary) rounded-lg opacity-60 cursor-not-allowed"
/>
</div>
</div>
<div>
<label className="block text-sm font-medium mb-1">Frontend Base URL *</label>
<label className="block text-sm font-medium mb-1 text-(--text-primary)">
Frontend Base URL *
</label>
<input
type="url"
value={formData.frontend_base_url}
onChange={(e) => setFormData({ ...formData, frontend_base_url: e.target.value })}
className="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
className="w-full px-3 py-2 bg-(--background) border border-(--card-border) text-(--text-primary) rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
placeholder="https://module.example.com"
required
/>
</div>
<div>
<label className="block text-sm font-medium mb-1">Backend Base URL *</label>
<label className="block text-sm font-medium mb-1 text-(--text-primary)">
Backend Base URL *
</label>
<input
type="url"
value={formData.backend_base_url}
onChange={(e) => setFormData({ ...formData, backend_base_url: e.target.value })}
className="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
className="w-full px-3 py-2 bg-(--background) border border-(--card-border) text-(--text-primary) rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
placeholder="https://api.module.example.com"
required
/>
@@ -154,93 +180,83 @@ const EnvironmentForm = ({ moduleId, environment, onClose }: EnvironmentFormProp
<div className="grid grid-cols-2 gap-4">
<div>
<label className="block text-sm font-medium mb-1">SSO Entry Path *</label>
<label className="block text-sm font-medium mb-1 text-(--text-primary)">
SSO Entry Path *
</label>
<input
type="text"
value={formData.sso_entry_path}
onChange={(e) => setFormData({ ...formData, sso_entry_path: e.target.value })}
className="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
className="w-full px-3 py-2 bg-(--background) border border-(--card-border) text-(--text-primary) rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
required
/>
</div>
<div>
<label className="block text-sm font-medium mb-1">Permission Sync Endpoint *</label>
<label className="block text-sm font-medium mb-1 text-(--text-primary)">
Permission Sync Endpoint *
</label>
<input
type="text"
value={formData.permission_sync_endpoint}
onChange={(e) => setFormData({ ...formData, permission_sync_endpoint: e.target.value })}
className="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
className="w-full px-3 py-2 bg-(--background) border border-(--card-border) text-(--text-primary) rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
required
/>
</div>
</div>
<div>
<label className="block text-sm font-medium mb-1">Provisioning Endpoint *</label>
<label className="block text-sm font-medium mb-1 text-(--text-primary)">
Provisioning Endpoint *
</label>
<input
type="text"
value={formData.provisioning_endpoint}
onChange={(e) => setFormData({ ...formData, provisioning_endpoint: e.target.value })}
className="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
className="w-full px-3 py-2 bg-(--background) border border-(--card-border) text-(--text-primary) rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
required
/>
</div>
<div>
<label className="block text-sm font-medium mb-1">
{formData.trust_type === 'hmac' ? 'HMAC Secret' : 'Secret Key'} {!environment && '*'}
<label className="block text-sm font-medium mb-1 text-(--text-primary)">
HMAC Secret {!environment && "*"}
</label>
<input
type="password"
value={formData.hmac_secret}
onChange={(e) => setFormData({ ...formData, hmac_secret: e.target.value })}
className="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
className="w-full px-3 py-2 bg-(--background) border border-(--card-border) text-(--text-primary) rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
placeholder={environment ? "Leave blank to keep existing" : "Enter secret"}
required={!environment}
/>
<p className="text-xs text-gray-500 mt-1">Stored securely on backend</p>
<p className="text-xs text-(--text-secondary) mt-1">
Stored securely on backend
</p>
</div>
<div className="flex items-center gap-4">
<label className="flex items-center gap-2">
<label className="flex items-center gap-2 cursor-pointer">
<input
type="checkbox"
checked={formData.is_default}
onChange={(e) => setFormData({ ...formData, is_default: e.target.checked })}
className="rounded"
className="rounded border-(--card-border) bg-(--background)"
/>
<span className="text-sm">Set as default environment</span>
<span className="text-sm text-(--text-primary)">Set as default</span>
</label>
<label className="flex items-center gap-2">
<label className="flex items-center gap-2 cursor-pointer">
<input
type="checkbox"
checked={formData.is_active}
onChange={(e) => setFormData({ ...formData, is_active: e.target.checked })}
className="rounded"
className="rounded border-(--card-border) bg-(--background)"
/>
<span className="text-sm">Active</span>
<span className="text-sm text-(--text-primary)">Active</span>
</label>
</div>
<div className="flex gap-3 pt-4">
<button
type="submit"
disabled={loading}
className="flex-1 px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 disabled:opacity-50"
>
{loading ? 'Saving...' : (environment ? 'Update' : 'Create')}
</button>
<button
type="button"
onClick={() => onClose(false)}
className="flex-1 px-4 py-2 bg-gray-200 rounded-lg hover:bg-gray-300"
>
Cancel
</button>
</div>
</form>
</div>
</div>
</CustomModal>
);
};
+7
View File
@@ -75,6 +75,12 @@ const navItems: NavItem[] = [
path: "/admin/modules",
access: "modules.view",
},
{
icon: <LayoutGrid size={22} />,
name: "Logs",
path: "/logs",
access: "admin.logs.read",
},
{
icon: <Settings size={22} />,
name: "Settings",
@@ -111,6 +117,7 @@ const AppSidebar: React.FC = () => {
'Dropdowns': 'dropdowns',
'Settings': 'settings',
'Modules': 'modules',
'Logs': 'logs',
};
const key = keyMap[name];
+1
View File
@@ -73,6 +73,7 @@
"status": {
"active": "نشط",
"inactive": "غير نشط",
"default": "افتراضي",
"pending": "قيد الانتظار",
"approved": "موافق عليه",
"rejected": "مرفوض",
+1
View File
@@ -73,6 +73,7 @@
"status": {
"active": "Active",
"inactive": "Inactive",
"default": "Default",
"pending": "Pending",
"approved": "Approved",
"rejected": "Rejected",
+2
View File
@@ -4,6 +4,7 @@ import SignInPage from "../application/authentication/SignInPage";
import SignUpPage from "../application/authentication/SignUpPage";
import ResetPasswordPage from "../application/authentication/ResetPasswordPage";
import Roles from "../application/roles";
import LogsPage from "../application/logs/LogsPage";
import Dashboard from "../application/dashboard/Dashboard";
import ProfilePage from "../application/profile/ProfilePage";
import Tenants from "../application/tenants";
@@ -31,6 +32,7 @@ const AppRoutes = () => {
<Route path="/theme/*" element={<Theme />} />
<Route path="/settings" element={<SettingsPage />} />
<Route path="/users/*" element={<Users />} />
<Route path="/logs" element={<LogsPage />} />
{/* Admin Module Routes */}
<Route path="/admin/modules/*" element={<Modules />} />