update lisensi prosess
tests / ci (push) Has been cancelled

This commit is contained in:
Wian Drs
2026-08-25 13:43:55 +07:00
parent 1e80be180e
commit 60946ddf37
58 changed files with 2903 additions and 165 deletions
+16 -2
View File
@@ -14,9 +14,14 @@ import { request } from '@/routes/password';
type Props = {
status?: string;
canResetPassword: boolean;
canRegister: boolean;
};
export default function Login({ status, canResetPassword }: Props) {
export default function Login({
status,
canResetPassword,
canRegister,
}: Props) {
return (
<>
<Head title="Log in" />
@@ -92,7 +97,16 @@ export default function Login({ status, canResetPassword }: Props) {
</div>
<p className="text-center text-sm text-muted-foreground">
Contact your administrator to request access.
{canRegister ? (
<>
Belum memiliki tenant?{' '}
<TextLink href="/register">
Daftar RADIQ NDM
</TextLink>
</>
) : (
'Hubungi administrator untuk meminta akses.'
)}
</p>
</>
)}
+205
View File
@@ -0,0 +1,205 @@
import { Form, Head, Link } from '@inertiajs/react';
import { Cloud, Server } from 'lucide-react';
import { useState } from 'react';
import InputError from '@/components/input-error';
import PasswordInput from '@/components/password-input';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Spinner } from '@/components/ui/spinner';
type Deployment = 'managed_cloud' | 'self_hosted';
export default function Register({ passwordRules }: { passwordRules: string }) {
const [deployment, setDeployment] = useState<Deployment>('managed_cloud');
const [plan, setPlan] = useState('cloud_free');
return (
<>
<Head title="Daftar Tenant" />
<Form
action="/register"
method="post"
className="flex flex-col gap-5"
>
{({ processing, errors }) => (
<>
<div className="grid gap-2">
<Label htmlFor="tenant_name">
Nama ISP / Perusahaan
</Label>
<Input
id="tenant_name"
name="tenant_name"
required
autoFocus
placeholder="Contoh: ISP Mekarmanah"
/>
<InputError message={errors.tenant_name} />
</div>
<div className="grid grid-cols-2 gap-3">
{(
[
[
'managed_cloud',
Cloud,
'Cloud RADIQ',
'Langsung gunakan aplikasi tanpa mengelola server.',
],
[
'self_hosted',
Server,
'Install Local',
'Jalankan di VPS, server, atau komputer sendiri.',
],
] as const
).map(([value, Icon, title, description]) => (
<button
key={value}
type="button"
onClick={() => {
setDeployment(value);
setPlan(
value === 'managed_cloud'
? 'cloud_free'
: 'local_micro',
);
}}
className={`rounded-xl border p-3 text-left transition ${deployment === value ? 'border-primary bg-primary/5 ring-1 ring-primary' : 'hover:bg-muted'}`}
>
<Icon className="mb-2 size-5 text-primary" />
<span className="block text-sm font-semibold">
{title}
</span>
<span className="mt-1 block text-xs text-muted-foreground">
{description}
</span>
</button>
))}
<input
type="hidden"
name="deployment_type"
value={deployment}
/>
<InputError message={errors.deployment_type} />
</div>
<div className="grid gap-2">
<Label htmlFor="plan_code">Paket lisensi</Label>
<select
id="plan_code"
name="plan_code"
value={plan}
onChange={(event) =>
setPlan(event.target.value)
}
className="h-10 rounded-md border bg-background px-3"
>
{deployment === 'managed_cloud' ? (
<>
<option value="cloud_free">
Cloud Free · Gratis · 5 perangkat ·
2 user
</option>
<option value="cloud_micro">
Cloud Micro · Rp99.000/bulan
</option>
<option value="cloud_isp">
Cloud ISP · Rp249.000/bulan
</option>
</>
) : (
<>
<option value="local_micro">
Local Micro · Rp69.000/bulan
</option>
<option value="local_isp">
Local ISP · Rp149.000/bulan
</option>
</>
)}
</select>
<InputError message={errors.plan_code} />
</div>
{deployment === 'self_hosted' && (
<div className="grid gap-2">
<Label htmlFor="deployment_domain">
Domain / alamat server (opsional)
</Label>
<Input
id="deployment_domain"
name="deployment_domain"
placeholder="ndm.isp-anda.co.id"
/>
<InputError
message={errors.deployment_domain}
/>
</div>
)}
<div className="grid gap-2">
<Label htmlFor="name">Nama Tenant Admin</Label>
<Input
id="name"
name="name"
required
autoComplete="name"
/>
<InputError message={errors.name} />
</div>
<div className="grid gap-2">
<Label htmlFor="email">Email</Label>
<Input
id="email"
name="email"
type="email"
required
autoComplete="email"
/>
<InputError message={errors.email} />
</div>
<div className="grid gap-2">
<Label htmlFor="password">Password</Label>
<PasswordInput
id="password"
name="password"
required
autoComplete="new-password"
/>
<p className="text-xs text-muted-foreground">
{passwordRules}
</p>
<InputError message={errors.password} />
</div>
<div className="grid gap-2">
<Label htmlFor="password_confirmation">
Konfirmasi Password
</Label>
<PasswordInput
id="password_confirmation"
name="password_confirmation"
required
autoComplete="new-password"
/>
</div>
<Button disabled={processing}>
{processing && <Spinner />} Buat Tenant
</Button>
<p className="text-center text-sm text-muted-foreground">
Sudah terdaftar?{' '}
<Link
href="/login"
className="font-medium text-primary hover:underline"
>
Login
</Link>
</p>
</>
)}
</Form>
</>
);
}
Register.layout = {
title: 'Daftarkan Tenant RADIQ NDM',
description: 'Pilih Cloud RADIQ atau instalasi di server milik Anda.',
};