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";
|
import { z } from "zod";
|
||||||
|
|
||||||
export const courseEventAttendeeSchema = z.object({
|
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_bookwork: z.boolean(),
|
||||||
passed_qual: z.boolean(),
|
passed_qual: z.boolean(),
|
||||||
remarks: z.string(),
|
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({
|
export const trainingReportSchema = z.object({
|
||||||
@@ -19,5 +19,42 @@ export const trainingReportSchema = z.object({
|
|||||||
),
|
),
|
||||||
remarks: z.string().nullable().optional(),
|
remarks: z.string().nullable().optional(),
|
||||||
attendees: z.array(courseEventAttendeeSchema).default([]),
|
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.",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,3 @@ VITE_ENVIRONMENT= # dev / prod
|
|||||||
# Glitchtip
|
# Glitchtip
|
||||||
VITE_GLITCHTIP_DSN=
|
VITE_GLITCHTIP_DSN=
|
||||||
VITE_DISABLE_GLITCHTIP= # true/false
|
VITE_DISABLE_GLITCHTIP= # true/false
|
||||||
|
|
||||||
# Matomo
|
|
||||||
VITE_DISABLE_MATOMO= # true/false
|
|
||||||
27
ui/package-lock.json
generated
27
ui/package-lock.json
generated
@@ -8,7 +8,6 @@
|
|||||||
"name": "milsimsitev4",
|
"name": "milsimsitev4",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@certible/use-matomo": "^1.1.0",
|
|
||||||
"@fullcalendar/core": "^6.1.19",
|
"@fullcalendar/core": "^6.1.19",
|
||||||
"@fullcalendar/daygrid": "^6.1.19",
|
"@fullcalendar/daygrid": "^6.1.19",
|
||||||
"@fullcalendar/interaction": "^6.1.19",
|
"@fullcalendar/interaction": "^6.1.19",
|
||||||
@@ -511,12 +510,6 @@
|
|||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@certible/use-matomo": {
|
|
||||||
"version": "1.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@certible/use-matomo/-/use-matomo-1.1.0.tgz",
|
|
||||||
"integrity": "sha512-9kOTj0ldP+RX2Rhlosxm/+1uQ2deDefMb4p2DQGFi3Gqi3TuTSrOtx+beUNKGN7D8up/8SsBPJS+ipY1ZHMfrA==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/aix-ppc64": {
|
"node_modules/@esbuild/aix-ppc64": {
|
||||||
"version": "0.25.9",
|
"version": "0.25.9",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz",
|
||||||
@@ -3601,13 +3594,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/tinyglobby": {
|
"node_modules/tinyglobby": {
|
||||||
"version": "0.2.15",
|
"version": "0.2.14",
|
||||||
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
|
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz",
|
||||||
"integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
|
"integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"fdir": "^6.5.0",
|
"fdir": "^6.4.4",
|
||||||
"picomatch": "^4.0.3"
|
"picomatch": "^4.0.2"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12.0.0"
|
"node": ">=12.0.0"
|
||||||
@@ -3735,17 +3728,17 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/vite": {
|
"node_modules/vite": {
|
||||||
"version": "7.2.4",
|
"version": "7.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-7.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-7.1.2.tgz",
|
||||||
"integrity": "sha512-NL8jTlbo0Tn4dUEXEsUg8KeyG/Lkmc4Fnzb8JXN/Ykm9G4HNImjtABMJgkQoVjOBN/j2WAwDTRytdqJbZsah7w==",
|
"integrity": "sha512-J0SQBPlQiEXAF7tajiH+rUooJPo0l8KQgyg4/aMunNtrOa7bwuZJsJbDWzeljqQpgftxuq5yNJxQ91O9ts29UQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"esbuild": "^0.25.0",
|
"esbuild": "^0.25.0",
|
||||||
"fdir": "^6.5.0",
|
"fdir": "^6.4.6",
|
||||||
"picomatch": "^4.0.3",
|
"picomatch": "^4.0.3",
|
||||||
"postcss": "^8.5.6",
|
"postcss": "^8.5.6",
|
||||||
"rollup": "^4.43.0",
|
"rollup": "^4.43.0",
|
||||||
"tinyglobby": "^0.2.15"
|
"tinyglobby": "^0.2.14"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"vite": "bin/vite.js"
|
"vite": "bin/vite.js"
|
||||||
|
|||||||
@@ -12,7 +12,6 @@
|
|||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@certible/use-matomo": "^1.1.0",
|
|
||||||
"@fullcalendar/core": "^6.1.19",
|
"@fullcalendar/core": "^6.1.19",
|
||||||
"@fullcalendar/daygrid": "^6.1.19",
|
"@fullcalendar/daygrid": "^6.1.19",
|
||||||
"@fullcalendar/interaction": "^6.1.19",
|
"@fullcalendar/interaction": "^6.1.19",
|
||||||
|
|||||||
@@ -18,10 +18,12 @@ import FieldSet from '../ui/field/FieldSet.vue'
|
|||||||
import FieldLegend from '../ui/field/FieldLegend.vue'
|
import FieldLegend from '../ui/field/FieldLegend.vue'
|
||||||
import FieldDescription from '../ui/field/FieldDescription.vue'
|
import FieldDescription from '../ui/field/FieldDescription.vue'
|
||||||
import Checkbox from '../ui/checkbox/Checkbox.vue'
|
import Checkbox from '../ui/checkbox/Checkbox.vue'
|
||||||
|
import { configure } from 'vee-validate'
|
||||||
|
|
||||||
|
|
||||||
const { handleSubmit, resetForm, errors, values, setFieldValue } = useForm({
|
const { handleSubmit, resetForm, errors, values, setFieldValue } = useForm({
|
||||||
validationSchema: toTypedSchema(trainingReportSchema),
|
validationSchema: toTypedSchema(trainingReportSchema),
|
||||||
|
validateOnMount: false,
|
||||||
initialValues: {
|
initialValues: {
|
||||||
course_id: null,
|
course_id: null,
|
||||||
event_date: "",
|
event_date: "",
|
||||||
@@ -129,8 +131,17 @@ onMounted(async () => {
|
|||||||
|
|
||||||
<VeeFieldArray name="attendees" v-slot="{ fields, push, remove }">
|
<VeeFieldArray name="attendees" v-slot="{ fields, push, remove }">
|
||||||
<FieldSet class="gap-4">
|
<FieldSet class="gap-4">
|
||||||
<FieldLegend class="scroll-m-20 text-lg tracking-tight">Attendees</FieldLegend>
|
<div class="flex flex-col gap-2">
|
||||||
<FieldDescription>Add members who attended this session.</FieldDescription>
|
<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">
|
<FieldGroup class="gap-4">
|
||||||
|
|
||||||
|
|||||||
@@ -9,10 +9,14 @@ import FormCheckbox from './components/form/FormCheckbox.vue'
|
|||||||
import FormInput from './components/form/FormInput.vue'
|
import FormInput from './components/form/FormInput.vue'
|
||||||
|
|
||||||
import * as Sentry from "@sentry/vue";
|
import * as Sentry from "@sentry/vue";
|
||||||
import { initMatomo } from "@certible/use-matomo"
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const app = createApp(App)
|
const app = createApp(App)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
app.use(createPinia())
|
app.use(createPinia())
|
||||||
app.use(router)
|
app.use(router)
|
||||||
|
|
||||||
@@ -32,18 +36,7 @@ if (!!import.meta.env.VITE_DISABLE_GLITCHTIP) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!!import.meta.env.VITE_DISABLE_MATOMO) {
|
|
||||||
console.log("Matomo init")
|
|
||||||
app.use(initMatomo({
|
|
||||||
host: 'https://stats.nexuszone.net/',
|
|
||||||
siteId: 4,
|
|
||||||
trackRouter: true,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
app.component("FormInput", FormInput)
|
app.component("FormInput", FormInput)
|
||||||
app.component("FormCheckbox", FormCheckbox)
|
app.component("FormCheckbox", FormCheckbox)
|
||||||
|
|
||||||
app.mount('#app')
|
app.mount('#app')
|
||||||
|
|
||||||
// window._paq.push(['trackPageView']);
|
|
||||||
Reference in New Issue
Block a user