-
-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Creates a new project locally. For some reason, BuildEngine is unhappy, even though it is allegedly receiving the exact same information from S1.
- Loading branch information
Showing
7 changed files
with
214 additions
and
4 deletions.
There are no files selected for viewing
This file contains 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
This file contains 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
This file contains 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
This file contains 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
This file contains 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
111 changes: 111 additions & 0 deletions
111
...L.AppBuilder.Portal/src/routes/(authenticated)/projects/new/[id=idNumber]/+page.server.ts
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
import { idSchema } from '$lib/valibot'; | ||
import { error } from '@sveltejs/kit'; | ||
import { DatabaseWrites, prisma } from 'sil.appbuilder.portal.common'; | ||
import { fail, superValidate } from 'sveltekit-superforms'; | ||
import { valibot } from 'sveltekit-superforms/adapters'; | ||
import * as v from 'valibot'; | ||
import type { Session } from '@auth/sveltekit'; | ||
import type { Actions, PageServerLoad } from './$types'; | ||
import { RoleId } from 'sil.appbuilder.portal.common/prisma'; | ||
import { scriptoriaQueue, BullMQ } from 'sil.appbuilder.portal.common'; | ||
import { time } from 'console'; | ||
|
||
const projectCreateSchema = v.object({ | ||
name: v.pipe(v.string(), v.minLength(1)), | ||
group: idSchema, | ||
language: v.pipe(v.string(), v.minLength(1)), | ||
type: idSchema, | ||
description: v.nullable(v.string()), | ||
public: v.boolean() | ||
}); | ||
|
||
export const load = (async ({ locals, params }) => { | ||
if (!verifyCanCreateProject((await locals.auth())!, parseInt(params.id))) return error(403); | ||
|
||
const organization = await prisma.organizations.findUnique({ | ||
where: { | ||
Id: parseInt(params.id) | ||
}, | ||
select: { | ||
Groups: { | ||
select: { | ||
Id: true, | ||
Name: true | ||
} | ||
}, | ||
OrganizationProductDefinitions: { | ||
select: { | ||
ProductDefinition: { | ||
select: { | ||
ApplicationTypes: true | ||
} | ||
} | ||
} | ||
}, | ||
PublicByDefault: true | ||
} | ||
}); | ||
|
||
const types = organization?.OrganizationProductDefinitions.map( | ||
(opd) => opd.ProductDefinition.ApplicationTypes | ||
).reduce((p, c) => { | ||
if (!p.some((e) => e.Id === c.Id)) { | ||
p.push(c); | ||
} | ||
return p; | ||
}, [] as { Id: number; Name: string | null; Description: string | null }[]); | ||
|
||
const form = await superValidate( | ||
{ | ||
group: organization?.Groups[0]?.Id ?? undefined, | ||
type: types?.[0].Id ?? undefined, | ||
public: organization?.PublicByDefault ?? undefined | ||
}, | ||
valibot(projectCreateSchema) | ||
); | ||
return { form, organization, types }; | ||
}) satisfies PageServerLoad; | ||
|
||
export const actions: Actions = { | ||
default: async function (event) { | ||
const session = (await event.locals.auth())!; | ||
if (!verifyCanCreateProject(session, parseInt(event.params.id))) return error(403); | ||
|
||
const form = await superValidate(event.request, valibot(projectCreateSchema)); | ||
// TODO: Return/Display error messages | ||
if (!form.valid) return fail(400, { form, ok: false }); | ||
if (isNaN(parseInt(event.params.id))) return fail(400, { form, ok: false }); | ||
const timestamp = (new Date()).toString(); | ||
const project = await DatabaseWrites.projects.create({ | ||
OrganizationId: parseInt(event.params.id), | ||
Name: form.data.name, | ||
GroupId: form.data.group, | ||
OwnerId: session.user.userId, | ||
Language: form.data.language, | ||
TypeId: form.data.type, | ||
Description: form.data.description ?? '', | ||
DateCreated: timestamp, | ||
DateUpdated: timestamp | ||
// TODO: DateActive? | ||
}); | ||
|
||
if (project !== false) { | ||
scriptoriaQueue.add(`Create Project #${project}`, { | ||
type: BullMQ.ScriptoriaJobType.CreateProject, | ||
projectId: project as number | ||
}); | ||
} | ||
|
||
return { form, ok: project !== false }; | ||
} | ||
}; | ||
|
||
async function verifyCanCreateProject(user: Session, orgId: number) { | ||
// Creating a project is allowed if the user is an OrgAdmin, AppBuilder, or SuperAdmin for the organization | ||
const roles = user.user.roles.filter(([org, role]) => org === orgId).map(([org, role]) => role); | ||
return ( | ||
roles.includes(RoleId.AppBuilder) || | ||
roles.includes(RoleId.OrgAdmin) || | ||
roles.includes(RoleId.SuperAdmin) | ||
); | ||
} |
86 changes: 86 additions & 0 deletions
86
.../SIL.AppBuilder.Portal/src/routes/(authenticated)/projects/new/[id=idNumber]/+page.svelte
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
<script lang="ts"> | ||
import { goto } from '$app/navigation'; | ||
import { page } from '$app/stores'; | ||
import LanguageCodeTypeahead from '$lib/components/LanguageCodeTypeahead.svelte'; | ||
import * as m from '$lib/paraglide/messages'; | ||
import { superForm } from 'sveltekit-superforms'; | ||
import type { PageData } from './$types'; | ||
export let data: PageData; | ||
const { form, enhance } = superForm(data.form, { | ||
dataType: 'json', | ||
onUpdated(event) { | ||
if (event.form.valid) { | ||
goto('/projects/' + $page.params.id); | ||
} | ||
} | ||
}); | ||
</script> | ||
|
||
<div class="w-full max-w-6xl mx-auto relative p-2"> | ||
<form action="" method="post" use:enhance> | ||
<h1>{m.project_newProject()}</h1> | ||
<div class="grid gap-2 gap-x-8 gridcont"> | ||
<div class="w-full flex place-content-between"> | ||
<label for="name">{m.project_projectName()}:</label> | ||
<input type="text" id="name" class="input input-bordered" bind:value={$form.name} /> | ||
</div> | ||
<div class="w-full flex place-content-between"> | ||
<label for="group">{m.project_projectGroup()}:</label> | ||
<select name="group" id="group" class="select select-bordered" bind:value={$form.group}> | ||
{#each data.organization?.Groups ?? [] as group} | ||
<option value={group.Id}>{group.Name}</option> | ||
{/each} | ||
</select> | ||
</div> | ||
<div class="w-full flex place-content-between"> | ||
<label for="language">{m.project_languageCode()}:</label> | ||
<!-- <input type="text" id="language" class="input input-bordered" bind:value={$form.language} /> --> | ||
<LanguageCodeTypeahead bind:langCode={$form.language} dropdownClasses="right-0" /> | ||
</div> | ||
<div class="w-full flex place-content-between"> | ||
<label for="type">{m.project_type()}:</label> | ||
<select name="type" id="type" class="select select-bordered" bind:value={$form.type}> | ||
{#each data.types ?? [] as type} | ||
<option value={type.Id}>{type.Description}</option> | ||
{/each} | ||
</select> | ||
</div> | ||
<div class="mt-4"> | ||
<label for="description"> | ||
{m.project_projectDescription()} | ||
<textarea | ||
name="description" | ||
id="description" | ||
class="textarea textarea-bordered w-full" | ||
bind:value={$form.description} | ||
/> | ||
</label> | ||
</div> | ||
<div class="w-full flex flex-col"> | ||
<div class="form-control"> | ||
<label for="public" class="label">{m.project_public()}: | ||
<input type="checkbox" name="public" id="public" class="toggle" bind:checked={$form.public} /> | ||
</label> | ||
</div> | ||
<p>{m.project_visibilityDescription()}</p> | ||
</div> | ||
</div> | ||
<div class="flex place-content-end space-x-2"> | ||
<a href="/projects/{$page.params.id}" class="btn">{m.common_cancel()}</a> | ||
<button class="btn btn-primary" type="submit">{m.common_save()}</button> | ||
</div> | ||
</form> | ||
</div> | ||
|
||
<style> | ||
.gridcont { | ||
grid-template-columns: repeat(auto-fill, minmax(48%, 1fr)); | ||
} | ||
.gridcont div.flex label { | ||
margin-right: 0.4rem; | ||
} | ||
.label[for="public"] { | ||
padding-left: 0px; | ||
} | ||
</style> |