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
13 changes: 13 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# editorconfig.org
root = true

[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

[*.md]
trim_trailing_whitespace = false
11 changes: 11 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ on:

permissions:
contents: read # for checkout
packages: read

jobs:
commitlint:
Expand All @@ -23,6 +24,10 @@ jobs:
uses: actions/setup-node@v4
with:
node-version: v22.12.0
- name: Create .npmrc
run: |
echo "@OrdinarySMP:registry=https://npm.pkg.github.com" > .npmrc
echo "//npm.pkg.github.com/:_authToken=${{ secrets.GITHUB_TOKEN }}" >> .npmrc
- name: Install commitlint
run: yarn add commitlint@latest conventional-changelog-conventionalcommits @commitlint/config-conventional
- name: Validate current commit (last commit) with commitlint
Expand All @@ -41,8 +46,14 @@ jobs:
uses: actions/setup-node@v4
with:
node-version: v22.12.0
- name: Create .npmrc
run: |
echo "@OrdinarySMP:registry=https://npm.pkg.github.com" > .npmrc
echo "//npm.pkg.github.com/:_authToken=${{ secrets.GITHUB_TOKEN }}" >> .npmrc
- name: Install dependencies
run: yarn install --frozen-lockfile
- name: Run typecheck
run: yarn typecheck
- name: Run linter
run: yarn eslint-ci
- name: Run prettier
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ logs
.env
.env.*
!.env.example
.npmrc
7 changes: 7 additions & 0 deletions app/app.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export default defineAppConfig({
ui: {
colors: {
brand: "brand",
},
},
});
7 changes: 7 additions & 0 deletions app/app.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<template>
<UApp>
<NuxtLayout>
<NuxtPage />
</NuxtLayout>
</UApp>
</template>
30 changes: 30 additions & 0 deletions app/assets/css/main.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
@import "tailwindcss";
@import "@nuxt/ui";

@theme {
--color-brand-50: #fff3ea;
--color-brand-100: #ffe1cc;
--color-brand-200: #ffc599;
--color-brand-300: #ffa866;
--color-brand-400: #f88b47;
--color-brand-500: #f0833a;
--color-brand-600: #d66d2a;
--color-brand-700: #b8581f;
--color-brand-800: #964518;
--color-brand-900: #6e3212;
--color-brand-950: #3a1808;
}

:root {
--color-primary-50: var(--color-brand-50);
--color-primary-100: var(--color-brand-100);
--color-primary-200: var(--color-brand-200);
--color-primary-300: var(--color-brand-300);
--color-primary-400: var(--color-brand-400);
--color-primary-500: var(--color-brand-500);
--color-primary-600: var(--color-brand-600);
--color-primary-700: var(--color-brand-700);
--color-primary-800: var(--color-brand-800);
--color-primary-900: var(--color-brand-900);
--color-primary-950: var(--color-brand-950);
}
File renamed without changes
File renamed without changes
File renamed without changes.
68 changes: 68 additions & 0 deletions app/components/TextChannelSelector.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<script lang="ts" setup>
import { ref } from "vue";
import type { ButtonProps } from "@nuxt/ui";

withDefaults(
defineProps<{
title: string;
size?: ButtonProps["size"];
}>(),
{
size: "sm",
},
);

const open = ref(false);
const channel = ref<string>("");
const textChannels = ref(await loadTextChannels());

const send = () => {
emit("select", channel.value);
open.value = false;
};

const emit = defineEmits<{
(e: "select", value: string): void;
}>();
</script>

<template>
<UModal
v-model:open="open"
:title="title"
description="Please select a channel."
>
<UButton
:size="size"
color="secondary"
type="button"
variant="subtle"
:label="title"
/>

<template #body>
<div class="space-y-4">
<UFormField label="Text Channel" name="channel" required>
<USelectMenu
v-model="channel"
:items="textChannels"
class="w-full"
value-key="value"
label-key="label"
/>
</UFormField>

<div class="flex justify-end gap-2">
<UButton
label="Cancel"
color="neutral"
variant="subtle"
size="md"
@click="open = false"
/>
<UButton label="Select" size="md" @click="send" />
</div>
</div>
</template>
</UModal>
</template>
124 changes: 124 additions & 0 deletions app/components/application/action-cell.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
<script lang="ts" setup>
import type { ApplicationData } from "@OrdinarySMP/api-types";

const client = useApiClient();
const openDeleteModal = ref(false);
const toast = useSimpleToast();

const props = defineProps<{
data: ApplicationData;
}>();

const emit = defineEmits<{
(e: "deleted"): void;
}>();

const deleteApplication = async () => {
try {
await client(`/application/${props.data.id}`, {
method: "delete",
});
toast.success("The Application was deleted.");
emit("deleted");
} catch {
toast.error("An error occurred while deleting the Application.");
} finally {
openDeleteModal.value = false;
}
};

const sendButton = async () => {
if (
!props.data?.embed_channel_id ||
!props.data?.embed_title ||
!props.data?.embed_color ||
!props.data?.embed_description
) {
toast.error("Please fill all embed fields before sending the button.");
return;
}
const response = await client(`/application/${props.data.id}/send-button`);

if (response) {
toast.success("The button was send.");
} else {
toast.error("An error occoured while sending the button.");
}
};
</script>

<template>
<div class="space-x-2">
<UButton
v-if="hasPermissionTo('application.update')"
label="Edit"
size="md"
icon="material-symbols:edit-outline"
type="button"
variant="subtle"
:to="`/application/edit/${data.id}`"
/>
<UButton
v-if="hasPermissionTo('applicationQuestion.read')"
label="Questions"
size="md"
icon="material-symbols:contact-support-outline"
type="button"
variant="subtle"
:to="`/application/${data.id}/question`"
/>
<UButton
v-if="hasPermissionTo('applicationResponse.read')"
label="Responses"
size="md"
icon="material-symbols:send-outline"
type="button"
variant="subtle"
:to="`/application/${data.id}/response`"
/>
<UButton
v-if="hasPermissionTo('application.update')"
label="Send button"
size="md"
icon="material-symbols:send-outline"
type="button"
variant="subtle"
color="neutral"
@click="sendButton"
/>

<UModal
v-model:open="openDeleteModal"
:title="`Delete the Application: ${data?.name}?`"
:description="`Are you sure, this action cannot be undone.`"
>
<UButton
v-if="hasPermissionTo('application.delete')"
size="md"
label="Delete"
color="error"
icon="material-symbols:delete-outline"
type="button"
variant="subtle"
/>

<template #body>
<div class="flex justify-end gap-2">
<UButton
label="Cancel"
color="neutral"
variant="subtle"
size="md"
@click="openDeleteModal = false"
/>
<UButton
label="Delete"
color="error"
size="md"
@click="deleteApplication"
/>
</div>
</template>
</UModal>
</div>
</template>
54 changes: 54 additions & 0 deletions app/components/application/button-fields.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<script lang="ts" setup>
import type { ApplicationData } from "@OrdinarySMP/api-types";
const model = defineModel<Partial<ApplicationData>>({ required: true });
const textChannels = ref(await loadTextChannels());
const chip = computed(() => ({
backgroundColor: model.value.embed_color ?? undefined,
}));
</script>

<template>
<UFormField label="Channel" name="embed_channel_id">
<USelectMenu
v-model="model.embed_channel_id as string | undefined"
:items="textChannels"
class="w-full"
value-key="value"
label-key="label"
/>
</UFormField>
<UFormField label="Title" name="embed_title">
<UInput v-model="model.embed_title" class="w-full" />
</UFormField>
<UFormField label="Color" name="color">
<UPopover>
<UButton label="Choose color" color="neutral" variant="outline">
<template #leading>
<span :style="chip" class="size-3 rounded-full" />
</template>
</UButton>

<template #content>
<UColorPicker
v-model="model.embed_color as string | undefined"
class="p-2"
/>
</template>
</UPopover>
</UFormField>
<UFormField label="Description" name="embed_description">
<UTextarea v-model="model.embed_description" class="w-full" />
</UFormField>
<UFormField label="Button color" name="embed_button_color">
<USelectMenu
v-model="model.embed_button_color"
:items="buttonOptions()"
class="w-full"
value-key="value"
label-key="label"
/>
</UFormField>
<UFormField label="Button Text" name="embed_button_text">
<UInput v-model="model.embed_button_text" class="w-full" />
</UFormField>
</template>
13 changes: 13 additions & 0 deletions app/components/application/fields.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<script lang="ts" setup>
import type { FaqData } from "@OrdinarySMP/api-types";
const model = defineModel<Partial<FaqData>>({ required: true });
</script>

<template>
<UFormField label="Question" name="question" required>
<UInput v-model="model.question" class="w-full" />
</UFormField>
<UFormField label="Answer" name="answer" required>
<UTextarea v-model="model.answer" class="w-full" />
</UFormField>
</template>
Loading