Merge pull request 'super amdin loign role creation in the saas' (#5) from ribai-patch-1 into dev

Reviewed-on: https://gitea.maskantech.in/gitea_admin/docqube_frontend/pulls/5
This commit is contained in:
2026-09-09 18:18:10 +00:00
5 changed files with 104 additions and 10 deletions
+20 -1
View File
@@ -36,12 +36,31 @@ import ConversionWorkflow from '@/features/conversion/ConversionWorkflow';
import UpgradePlan from '@/features/subscription/SubscriptionPage';
import { useAuth } from '@/features/auth/context';
const RouteFallback = () => (
<div className="flex min-h-[60vh] items-center justify-center" role="status" aria-live="polite">
<div className="h-8 w-8 animate-spin rounded-full border-2 border-gray-300 border-t-transparent" />
<span className="sr-only">Loading</span>
</div>
);
const TenantDashboardRoute = ({ children }: { children: React.ReactElement }) => {
const { user, isLoading } = useAuth();
if (isLoading) {
return <RouteFallback />;
}
const isSuperadmin = Boolean(
user?.is_superadmin ||
user?.role === 'superadmin' ||
user?.accesses?.includes('superadmin.main.view')
);
if (isSuperadmin) {
return <Navigate to="/admin/dashboard" replace />;
}
return children;
};
// Scroll to top component
const ScrollToTop = () => {
const { pathname } = useLocation();
@@ -157,7 +176,7 @@ function AppContent() {
</RequireAuth>
}
>
<Route path="/dashboard" element={<DashboardMainView />} />
<Route path="/dashboard" element={<TenantDashboardRoute><DashboardMainView /></TenantDashboardRoute>} />
<Route path="/documents" element={<RequireAccess accessCode="document.read"><MyDocumentsView /></RequireAccess>} />
<Route
path="/new-conversion"
+1
View File
@@ -76,6 +76,7 @@ export const authAPI = {
saasSignInRes = await fetch(`${saasApiUrl}/api/auth/signin`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify({ email: credentials.email.trim(), password: credentials.password }),
});
} catch (e: any) {
@@ -13,6 +13,7 @@ import ConfirmationModal from '@/components/custom/CustomConfirmationModal';
import { tenantApi, Tenant } from '@/services/tenant';
import { useAuth } from '@/features/auth/context';
import { useTranslation } from 'react-i18next';
import { getSaasFrontendUrl } from '@/lib/saasConfig';
const RoleManagement = () => {
const [roles, setRoles] = useState<Role[]>([]);
@@ -77,12 +78,17 @@ const RoleManagement = () => {
}, []);
const handleOpenCreate = () => {
if (isTenantAdmin) {
const saasUrl = getSaasFrontendUrl();
window.open(`${saasUrl}/roles/add`, '_blank');
return;
}
setIsEditing(false);
setIsViewModalOpen(false);
setRoleName('');
setRoleDescription('');
setSelectedAccessIds([]);
setSelectedTenantId(isTenantAdmin ? user!.tenant_id! : '');
setSelectedTenantId('');
setIsSuperadminRole(false);
setIsDefaultRole(false);
setIsModalOpen(true);
@@ -321,6 +327,22 @@ const RoleManagement = () => {
</CustomButton>
</div>
{isTenantAdmin && (
<div className="mb-4 p-3 bg-blue-50 border border-blue-200 rounded-lg flex flex-wrap items-center justify-between gap-2 text-sm text-blue-800">
<div className="flex items-center gap-2">
<span>Roles and user permissions are centrally managed in Maskan SaaS Hub.</span>
</div>
<a
href={`${getSaasFrontendUrl()}/roles`}
target="_blank"
rel="noopener noreferrer"
className="font-semibold underline hover:text-blue-900 flex items-center gap-1"
>
Manage in SaaS Hub &rarr;
</a>
</div>
)}
<div className="flex-1 flex flex-col min-h-0">
<CustomTable
data={roles}
@@ -19,6 +19,7 @@ import { tenantApi, Tenant } from '@/services/tenant';
import { ConfigurationApi, LimitsSnapshot } from '@/features/superAdmin/settings/ConfigurationApi';
import { useAuth } from '@/features/auth/context';
import { useTranslation } from 'react-i18next';
import { getSaasFrontendUrl } from '@/lib/saasConfig';
export default function UserManagement() {
const [users, setUsers] = useState<UserListItem[]>([]);
@@ -166,6 +167,12 @@ export default function UserManagement() {
const openCreateModal = async () => {
if (isTenantAdmin) {
const saasUrl = getSaasFrontendUrl();
window.open(`${saasUrl}/users/add`, '_blank');
return;
}
setIsCreating(true);
setSelectedUser(null);
setFormName('');
@@ -180,14 +187,10 @@ export default function UserManagement() {
refreshLimits();
if (isTenantAdmin) {
await onTenantChanged(user!.tenant_id!);
} else {
setFormTenantId('');
setSelectedTenantActiveUsers(null);
setSelectedTenantUserLimit(null);
setAvailableRoles([]);
}
setFormTenantId('');
setSelectedTenantActiveUsers(null);
setSelectedTenantUserLimit(null);
setAvailableRoles([]);
setIsModalOpen(true);
};
@@ -702,6 +705,22 @@ export default function UserManagement() {
</div>
</div>
{isTenantAdmin && (
<div className="mb-6 p-3 bg-blue-50 border border-blue-200 rounded-lg flex flex-wrap items-center justify-between gap-2 text-sm text-blue-800">
<div className="flex items-center gap-2">
<span>User onboarding, invitations, and team assignments are centrally managed in Maskan SaaS Hub.</span>
</div>
<a
href={`${getSaasFrontendUrl()}/users`}
target="_blank"
rel="noopener noreferrer"
className="font-semibold underline hover:text-blue-900 flex items-center gap-1"
>
Manage in SaaS Hub &rarr;
</a>
</div>
)}
{(orgCreditStats || isLoading) && (
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 mb-8">
{isLoading && !orgCreditStats ? (
+33
View File
@@ -0,0 +1,33 @@
/**
* Helper to dynamically resolve the Central SaaS Management Frontend URL
* Supports local development, test environments, and production dynamically.
*/
export function getSaasFrontendUrl(): string {
const envSaasFrontend = (import.meta.env.VITE_SAAS_FRONTEND_URL as string | undefined);
if (envSaasFrontend && envSaasFrontend.trim()) {
return envSaasFrontend.trim().replace(/\/+$/, '');
}
if (typeof window !== 'undefined') {
const hostname = window.location.hostname;
if (hostname.includes('maskantech.in')) {
if (hostname.includes('test')) {
return 'https://saas-test.maskantech.in';
}
return 'https://saas.maskantech.in';
}
if (hostname === 'localhost' || hostname === '127.0.0.1') {
return 'http://localhost:5174';
}
}
const saasApi = (import.meta.env.VITE_SAAS_API_URL as string | undefined) || '';
if (saasApi.includes('8000') || saasApi.includes('localhost')) {
return 'http://localhost:5174';
}
if (saasApi.trim()) {
return saasApi.trim().replace(/\/+$/, '');
}
return 'http://localhost:5173';
}