Compare commits
2 Commits
Matomo-Int
...
Training-R
| Author | SHA1 | Date | |
|---|---|---|---|
| 41cdd0b74f | |||
| b40f37c959 |
62
.gitea/workflows/cd-deploy.yaml
Normal file
62
.gitea/workflows/cd-deploy.yaml
Normal file
@@ -0,0 +1,62 @@
|
||||
name: Continuous Deployment
|
||||
on:
|
||||
push:
|
||||
|
||||
jobs:
|
||||
Deploy:
|
||||
name: Update Deployment
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
volumes:
|
||||
- /var/www/html/milsim-site-v4:/var/www/html/milsim-site-v4:rw
|
||||
steps:
|
||||
- name: Setup Local Environment
|
||||
run: |
|
||||
groupadd -g 989 nginx || true
|
||||
useradd nginx -u 990 -g nginx -m || true
|
||||
|
||||
- name: Verify Node Environment
|
||||
run: |
|
||||
npm -v
|
||||
node -v
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: 'main'
|
||||
|
||||
- name: Token Copy
|
||||
run: |
|
||||
cd /var/www/html/milsim-site-v4
|
||||
cp /workspace/17th-Ranger-Battalion-ORG/milsim-site-v4/.git/config .git/config
|
||||
chown nginx:nginx .git/config
|
||||
|
||||
- name: Fix File Permissions
|
||||
run: |
|
||||
sudo chown -R nginx:nginx /var/www/html/milsim-site-v4
|
||||
sudo chmod -R u+w /var/www/html/milsim-site-v4
|
||||
|
||||
- name: Update Application Code
|
||||
run: |
|
||||
sudo -u nginx bash -c "cd /var/www/html/milsim-site-v4 && git reset --hard && git pull origin main"
|
||||
|
||||
- name: Update Shared Dependencies
|
||||
run: |
|
||||
sudo -u nginx bash -c "cd /var/www/html/milsim-site-v4/shared && npm install"
|
||||
|
||||
- name: Update UI Dependencies
|
||||
run: |
|
||||
sudo -u nginx bash -c "cd /var/www/html/milsim-site-v4/ui && npm install"
|
||||
|
||||
- name: Update API Dependencies
|
||||
run: |
|
||||
sudo -u nginx bash -c "cd /var/www/html/milsim-site-v4/api && npm install"
|
||||
|
||||
- name: Build UI
|
||||
run: |
|
||||
sudo -u nginx bash -c "cd /var/www/html/milsim-site-v4/ui && npm run build"
|
||||
|
||||
- name: Build API
|
||||
run: |
|
||||
sudo -u nginx bash -c "cd /var/www/html/milsim-site-v4/api && npm run build"
|
||||
@@ -1,11 +1,11 @@
|
||||
import { z } from "zod";
|
||||
|
||||
export const courseEventAttendeeSchema = z.object({
|
||||
attendee_id: z.number({invalid_type_error: "Must select a member"}).int().positive(),
|
||||
attendee_id: z.number({ invalid_type_error: "Must select a member" }).int().positive(),
|
||||
passed_bookwork: z.boolean(),
|
||||
passed_qual: z.boolean(),
|
||||
remarks: z.string(),
|
||||
attendee_role_id: z.number({invalid_type_error: "Must select a role"}).int().positive()
|
||||
attendee_role_id: z.number({ invalid_type_error: "Must select a role" }).int().positive()
|
||||
})
|
||||
|
||||
export const trainingReportSchema = z.object({
|
||||
@@ -19,5 +19,42 @@ export const trainingReportSchema = z.object({
|
||||
),
|
||||
remarks: z.string().nullable().optional(),
|
||||
attendees: z.array(courseEventAttendeeSchema).default([]),
|
||||
}).superRefine((data, ctx) => {
|
||||
const trainerRole = 1;
|
||||
const traineeRole = 2;
|
||||
|
||||
const hasTrainer = data.attendees.some((a) => a.attendee_role_id === trainerRole);
|
||||
const hasTrainee = data.attendees.some((a) => a.attendee_role_id === traineeRole);
|
||||
|
||||
if (!hasTrainer) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
path: ["attendees"],
|
||||
message: "At least one Primary Trainer is required.",
|
||||
});
|
||||
}
|
||||
|
||||
if (!hasTrainee) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
path: ["attendees"],
|
||||
message: "At least one Trainee is required.",
|
||||
});
|
||||
}
|
||||
|
||||
//no duplicates
|
||||
const idCounts = new Map<number, number>();
|
||||
|
||||
data.attendees.forEach((a, index) => {
|
||||
idCounts.set(a.attendee_id, (idCounts.get(a.attendee_id) ?? 0) + 1);
|
||||
|
||||
if (idCounts.get(a.attendee_id)! > 1) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
path: ["attendees"],
|
||||
message: "Cannot have duplicate attendee.",
|
||||
});
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -18,10 +18,12 @@ import FieldSet from '../ui/field/FieldSet.vue'
|
||||
import FieldLegend from '../ui/field/FieldLegend.vue'
|
||||
import FieldDescription from '../ui/field/FieldDescription.vue'
|
||||
import Checkbox from '../ui/checkbox/Checkbox.vue'
|
||||
import { configure } from 'vee-validate'
|
||||
|
||||
|
||||
const { handleSubmit, resetForm, errors, values, setFieldValue } = useForm({
|
||||
validationSchema: toTypedSchema(trainingReportSchema),
|
||||
validateOnMount: false,
|
||||
initialValues: {
|
||||
course_id: null,
|
||||
event_date: "",
|
||||
@@ -129,8 +131,17 @@ onMounted(async () => {
|
||||
|
||||
<VeeFieldArray name="attendees" v-slot="{ fields, push, remove }">
|
||||
<FieldSet class="gap-4">
|
||||
<FieldLegend class="scroll-m-20 text-lg tracking-tight">Attendees</FieldLegend>
|
||||
<FieldDescription>Add members who attended this session.</FieldDescription>
|
||||
<div class="flex flex-col gap-2">
|
||||
<FieldLegend class="scroll-m-20 text-lg tracking-tight mb-0">Attendees</FieldLegend>
|
||||
<FieldDescription class="mb-0">Add members who attended this session.</FieldDescription>
|
||||
<div class="h-4">
|
||||
<div class="text-red-500 text-sm"
|
||||
v-if="errors.attendees && typeof errors.attendees === 'string'">
|
||||
{{ errors.attendees }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<FieldGroup class="gap-4">
|
||||
|
||||
|
||||
Reference in New Issue
Block a user