Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions apps/app/src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ import { MainNav } from "~/components/ui/main-nav";
import { GenerateToken } from "~/components/generate-token";
import { ExtractStartForm } from "~/components/extract-start-form";
import { TransformStartForm } from "~/components/transform-start-form";
import { getEnvTenants } from "~/components/tenant.env";

export default function Page() {
const tenants = getEnvTenants(); // getServerSideProps is deprecated. createServerContext documentation lackluster
return (
<>
<div className="flex-col">
Expand All @@ -19,8 +21,8 @@ export default function Page() {
</div>
</div>
<GenerateToken />
<ExtractStartForm/>
<TransformStartForm/>
<ExtractStartForm TENANTS={tenants}/>
<TransformStartForm TENANTS={tenants}/>
</>
);
}
}
18 changes: 15 additions & 3 deletions apps/app/src/components/extract-start-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,13 @@ import { useAuth } from '@clerk/nextjs';
import { useState } from 'react';
import type { ChangeEvent, Dispatch, SetStateAction } from 'react';
import { Input } from './ui/input';
import { TenantPicker } from './tenant-picker';
import type { Tenant } from './tenant.env';


export function ExtractStartForm() {
type ExtractStartFormProps = {
TENANTS: Tenant[] | undefined
}
export function ExtractStartForm({ TENANTS }: ExtractStartFormProps) {
const { getToken } = useAuth();
const currentDate = new Date();
currentDate.setMonth(currentDate.getMonth() - 6);
Expand All @@ -18,20 +22,22 @@ export function ExtractStartForm() {
const [to, setTo] = useState(new Date().toISOString().slice(0, 10));
const [status, setStatus] = useState('---');
const [body, setBody] = useState('');
const [tenantId, setTenantId] = useState((TENANTS || [])[0]?.id || -1);

const handleInputChange = (stateSetter: Dispatch<SetStateAction<any>>) => (ev: ChangeEvent<HTMLInputElement>) => stateSetter(ev.target.value);

const handleSelectChange = (ev: ChangeEvent<HTMLSelectElement>) => setSourceControl(ev.target.value);

const handleSubmit = async () => {
if (!process.env.NEXT_PUBLIC_EXTRACT_API_URL) return console.error('Missing ENV variable: NEXT_PUBLIC_EXTRACT_API_URL');
if (tenantId === -1) return console.error(`Invalid ENV variable: TENANTS`);
setStatus('...');
setBody('');

const token = await getToken({ template: 'dashboard' });
if (!token) return;

const requestBody = JSON.stringify({ repositoryId, repositoryName, namespaceName, sourceControl, from: new Date(from), to: new Date(to) });
const requestBody = JSON.stringify({ repositoryId, repositoryName, namespaceName, sourceControl, from: new Date(from), to: new Date(to), tenantId });

const res = await fetch(process.env.NEXT_PUBLIC_EXTRACT_API_URL, {
method: 'post',
Expand Down Expand Up @@ -81,6 +87,12 @@ export function ExtractStartForm() {
<td><input type='date' value={from} onChange={handleInputChange(setFrom)} className='flex h-9 w-full rounded-md border border-input bg-background px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50' /></td>
<td><input type='date' value={to} onChange={handleInputChange(setTo)} className='flex h-9 w-full rounded-md border border-input bg-background px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50' /></td>
</tr>
<tr>
<td>tenant:</td>
<td>
<TenantPicker tenants={TENANTS} setTenantId={setTenantId} />
</td>
</tr>
</tbody>
</table>
<button onClick={() => void handleSubmit()}>Submit</button>
Expand Down
25 changes: 25 additions & 0 deletions apps/app/src/components/tenant-picker.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
"use client"
import type { ChangeEvent } from "react";
import type { Tenant } from "./tenant.env";

type TenantPickerProps = {
tenants: Tenant[] | undefined
setTenantId: (tenantId: number) => void;
}
export function TenantPicker({ tenants, setTenantId }: TenantPickerProps) {

if (tenants === undefined) return (<span style={{color: 'red'}}>Invalid environment varibale: TENANTS</span>);

if (tenants.length === 0) return (<span style={{color: 'blue'}}>Empty tenancy config</span>);

const handleSelectChange = (ev: ChangeEvent<HTMLSelectElement>) => setTenantId(Number(ev.target.value));

return (
<select defaultValue={tenants[0]?.id} onChange={handleSelectChange} className='flex h-9 w-full rounded-md border border-input bg-background px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50'>
{tenants.map(t=>(
<option key={t.id} value={t.id}>{t.tenant}</option>
))}
</select>
);

}
34 changes: 34 additions & 0 deletions apps/app/src/components/tenant.env.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { z } from "zod";

const ENVSchema = z.object({
TENANTS: z.string(),
})
const TenantSchema = z.object({
id: z.number(),
tenant: z.string(),
dbUrl: z.string(),
});
const TenantArraySchema = z.array(TenantSchema);

const processEnv = () => {
const validEnv = ENVSchema.safeParse(process.env);
if (!validEnv.success) {
console.error(`Missing required environment variable 'TENANT'`);
return undefined;
}

const validTenantArray = TenantArraySchema.safeParse(JSON.parse(validEnv.data.TENANTS));
if (!validTenantArray.success) {
console.error("Invalid environment variable 'TENANTS' value:", ...validTenantArray.error.issues);
return undefined;
}

return validTenantArray.data;
}

const TENANTS = processEnv();


export const getEnvTenants = () => TENANTS;

export type Tenant = z.infer<typeof TenantSchema>;
14 changes: 12 additions & 2 deletions apps/app/src/components/transform-start-form.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,23 @@
"use client"
import { useAuth } from '@clerk/nextjs';
import { useState } from 'react';
import type { Tenant } from './tenant.env';
import { TenantPicker } from './tenant-picker';


export function TransformStartForm() {
type TransformStartFormProps = {
TENANTS: Tenant[] | undefined
}
export function TransformStartForm({ TENANTS }: TransformStartFormProps) {
const { getToken } = useAuth();
const [tenantId, setTenantId] = useState((TENANTS || [])[0]?.id || -1);
const [status, setStatus] = useState('---');
const [body, setBody] = useState('');

const handleClick = async () => {
if (!process.env.NEXT_PUBLIC_TRANSFORM_API_URL) return console.error('Missing ENV variable: NEXT_PUBLIC_TRANSFORM_API_URL');
if (tenantId === -1) return console.error(`Invalid ENV variable: TENANTS`);
const requestBody = JSON.stringify({ tenantId });

setStatus('...');
setBody('');

Expand All @@ -18,6 +26,7 @@ export function TransformStartForm() {

const res = await fetch(process.env.NEXT_PUBLIC_TRANSFORM_API_URL, {
method: 'post',
body: requestBody,
headers: {
'Authorization': 'Bearer ' + token
}
Expand All @@ -36,6 +45,7 @@ export function TransformStartForm() {
<button onClick={() => { handleClick().catch(console.log) }}>
Transform
</button>
<div>tenant: <span className="inline-block"><TenantPicker tenants={TENANTS} setTenantId={setTenantId} /></span></div>
</div>
<div><b>{status}</b></div>
<pre>{body}</pre>
Expand Down