Created
October 13, 2025 03:46
-
-
Save swdevbali/a00060845e6149c2d694e3deb1852b60 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 'use client'; | |
| import { useState, useEffect, Suspense } from 'react'; | |
| import { useRouter, useSearchParams } from 'next/navigation'; | |
| import { | |
| Building2, | |
| Rocket, | |
| Users, | |
| Video, | |
| ArrowRight, | |
| Loader2, | |
| UserPlus, | |
| Plus, | |
| Mail, | |
| ArrowLeft, | |
| Clock, | |
| AlertCircle | |
| } from 'lucide-react'; | |
| import { Button } from '@/components/ui/button'; | |
| import { Input } from '@/components/ui/input'; | |
| import { Label } from '@/components/ui/label'; | |
| import { toast } from 'sonner'; | |
| import { OrganizationServiceClient } from '@/lib/services/organizationServiceClient'; | |
| import { createClient } from '@/lib/supabase/client'; | |
| function CreateNewTenantContent() { | |
| const router = useRouter(); | |
| const searchParams = useSearchParams(); | |
| const [loading, setLoading] = useState(false); | |
| const [checkingPermissions, setCheckingPermissions] = useState(true); | |
| const [isSuperAdmin, setIsSuperAdmin] = useState(false); | |
| // Create organization fields | |
| const [organizationName, setOrganizationName] = useState(''); | |
| const [website, setWebsite] = useState(''); | |
| useEffect(() => { | |
| checkSuperAdminStatus(); | |
| }, []); | |
| const checkSuperAdminStatus = async () => { | |
| try { | |
| const supabase = createClient(); | |
| const { data: { user } } = await supabase.auth.getUser(); | |
| if (!user) { | |
| // Not authenticated - redirect to login | |
| router.push('/'); | |
| return; | |
| } | |
| // Check if user is superadmin | |
| const response = await fetch('/api/users/roles/current'); | |
| if (response.ok) { | |
| const data = await response.json(); | |
| const hasSuperAdmin = data.roles?.includes('superadmin') || false; | |
| setIsSuperAdmin(hasSuperAdmin); | |
| if (!hasSuperAdmin) { | |
| // Non-superadmin cannot create tenants | |
| toast.error('Only super administrators can create tenant organizations'); | |
| router.push('/dashboard'); | |
| return; | |
| } | |
| } | |
| // Superadmin confirmed - ready to create tenant | |
| setCheckingPermissions(false); | |
| } catch (error) { | |
| console.error('Error checking superadmin status:', error); | |
| setCheckingPermissions(false); | |
| } | |
| }; | |
| const handleCreateOrganization = async () => { | |
| if (!organizationName.trim()) { | |
| toast.error('Please enter an organization name'); | |
| return; | |
| } | |
| setLoading(true); | |
| try { | |
| const response = await fetch('/api/organizations/create', { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| }, | |
| body: JSON.stringify({ | |
| name: organizationName, | |
| website: website || null, | |
| }), | |
| }); | |
| const data = await response.json(); | |
| console.log('Create organization response:', response.status, data); | |
| if (!response.ok || !data.success) { | |
| throw new Error(data.message || 'Failed to create organization'); | |
| } | |
| toast.success('Organization created successfully!'); | |
| // Set as current organization | |
| if (data.organization?.id) { | |
| console.log('Setting current organization:', data.organization.id); | |
| OrganizationServiceClient.setCurrentOrganization(data.organization.id); | |
| } | |
| // Redirect to dashboard after successful creation | |
| console.log('Redirecting to dashboard...'); | |
| setTimeout(() => { | |
| router.push('/dashboard'); | |
| }, 1000); | |
| } catch (error) { | |
| console.error('Error creating organization:', error); | |
| toast.error(error instanceof Error ? error.message : 'Failed to create organization'); | |
| } finally { | |
| setLoading(false); | |
| } | |
| }; | |
| if (checkingPermissions) { | |
| return ( | |
| <div className="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-100 flex items-center justify-center"> | |
| <div className="flex flex-col items-center"> | |
| <Loader2 className="h-8 w-8 animate-spin text-blue-600 mb-4" /> | |
| <p className="text-gray-600">Verifying permissions...</p> | |
| </div> | |
| </div> | |
| ); | |
| } | |
| // Only superadmins should reach this page | |
| // Non-superadmins are redirected by middleware and checkSuperAdminStatus | |
| return ( | |
| <div className="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-100 flex items-center justify-center p-4"> | |
| <div className="max-w-lg w-full"> | |
| <div className="text-center mb-8"> | |
| <div className="inline-flex items-center justify-center w-16 h-16 bg-blue-600 rounded-full mb-4"> | |
| <Building2 className="w-8 h-8 text-white" /> | |
| </div> | |
| <h1 className="text-3xl font-bold text-gray-900 mb-2">Create Tenant</h1> | |
| <p className="text-gray-600">Set up a new tenant workspace for digital signage management</p> | |
| </div> | |
| <div className="bg-white rounded-2xl shadow-xl p-8"> | |
| <div className="space-y-6"> | |
| <div> | |
| <Label htmlFor="org-name" className="text-sm font-medium text-gray-700"> | |
| Tenant Organization Name * | |
| </Label> | |
| <Input | |
| id="org-name" | |
| type="text" | |
| placeholder="e.g., Acme Corporation" | |
| value={organizationName} | |
| onChange={(e) => setOrganizationName(e.target.value)} | |
| className="mt-1" | |
| disabled={loading} | |
| /> | |
| <p className="mt-1 text-xs text-gray-500"> | |
| The name of the tenant organization you're creating | |
| </p> | |
| </div> | |
| <div> | |
| <Label htmlFor="website" className="text-sm font-medium text-gray-700"> | |
| Website (optional) | |
| </Label> | |
| <Input | |
| id="website" | |
| type="url" | |
| placeholder="https://example.com" | |
| value={website} | |
| onChange={(e) => setWebsite(e.target.value)} | |
| className="mt-1" | |
| disabled={loading} | |
| /> | |
| <p className="mt-1 text-xs text-gray-500"> | |
| The tenant organization's website | |
| </p> | |
| </div> | |
| <div className="bg-blue-50 border border-blue-200 rounded-lg p-4"> | |
| <h3 className="font-semibold text-blue-900 mb-2">Tenant Organization Features:</h3> | |
| <ul className="space-y-1 text-sm text-blue-800"> | |
| <li>• Isolated data and content</li> | |
| <li>• Dedicated device management</li> | |
| <li>• Separate user management</li> | |
| <li>• Custom venue configurations</li> | |
| <li>• Independent license allocation</li> | |
| </ul> | |
| </div> | |
| <div className="flex gap-3"> | |
| <Button | |
| variant="outline" | |
| onClick={() => router.push('/dashboard')} | |
| disabled={loading} | |
| className="flex-1" | |
| > | |
| Cancel | |
| </Button> | |
| <Button | |
| onClick={handleCreateOrganization} | |
| disabled={loading || !organizationName.trim()} | |
| className="flex-1 bg-blue-600 hover:bg-blue-700 text-white" | |
| > | |
| {loading ? ( | |
| <> | |
| <Loader2 className="mr-2 h-4 w-4 animate-spin" /> | |
| Creating... | |
| </> | |
| ) : ( | |
| <> | |
| Create Tenant | |
| <ArrowRight className="ml-2 h-4 w-4" /> | |
| </> | |
| )} | |
| </Button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| ); | |
| } | |
| export default function CreateNewTenantPage() { | |
| return ( | |
| <Suspense fallback={ | |
| <div className="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-100 flex items-center justify-center"> | |
| <div className="flex flex-col items-center"> | |
| <Loader2 className="h-8 w-8 animate-spin text-blue-600 mb-4" /> | |
| <p className="text-gray-600">Loading...</p> | |
| </div> | |
| </div> | |
| }> | |
| <CreateNewTenantContent /> | |
| </Suspense> | |
| ); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment