23 Commits

Author SHA1 Message Date
2ac66877eb Merge branch 'main' into Calendar-Improvements 2025-12-09 21:21:03 -06:00
760965de19 Add .gitea/workflows/ci-deploy.yaml
Some checks failed
Continuous Integration / Update Development (push) Failing after 1m28s
create CI script based on CD script to run on every push to main and deploy on development site
2025-12-09 21:11:03 -06:00
37f204137b Update .gitea/workflows/cd-deploy.yaml
fix run, apparently on push triggers are not cumulatively conditional
2025-12-09 21:06:45 -06:00
58431891ba Update ecosystem.config.js
All checks were successful
Continuous Deployment / Update Deployment (push) Successful in 2m15s
allow automatic naming of started process based on environment
2025-12-09 20:55:41 -06:00
a95bf3318d Update .gitea/workflows/cd-deploy.yaml
All checks were successful
Continuous Deployment / Update Deployment (push) Successful in 2m5s
update the CD script to only run on tags signifying a usable release
2025-12-09 20:53:33 -06:00
6d544225d9 Update .gitea/workflows/cd-deploy.yaml
All checks were successful
Continuous Deployment / Update Deployment (push) Successful in 3m20s
fix syntax
2025-12-09 20:31:38 -06:00
ec854c6800 Update .gitea/workflows/cd-deploy.yaml
Some checks failed
Continuous Deployment / Update Deployment (push) Failing after 1m17s
update node version to 20 from built in 18
2025-12-09 20:26:13 -06:00
8089ee4268 Update .gitea/workflows/cd-deploy.yaml
Some checks failed
Continuous Deployment / Update Deployment (push) Failing after 1m48s
stupidly work out permissions error due to sudo not copying environment variables properly
2025-12-09 20:22:08 -06:00
468fd30514 Update .gitea/workflows/cd-deploy.yaml
Some checks failed
Continuous Deployment / Update Deployment (push) Failing after 1m27s
attempt to preserve existing environment to fix node issues
2025-12-09 14:55:40 -06:00
1f52a2c4f7 Update .gitea/workflows/cd-deploy.yaml
Some checks failed
Continuous Deployment / Update Deployment (push) Failing after 1m27s
forcefully override npm path for execution
2025-12-09 14:48:19 -06:00
dccdaddd20 Update .gitea/workflows/cd-deploy.yaml
Some checks failed
Continuous Deployment / Update Deployment (push) Failing after 1m28s
another deployment attempt
2025-12-09 14:45:24 -06:00
1a80bfc543 Update .gitea/workflows/cd-deploy.yaml
Some checks failed
Continuous Deployment / Update Deployment (push) Failing after 1m33s
continue to debug workflow
2025-12-09 14:37:16 -06:00
2185ffc746 Update .gitea/workflows/cd-deploy.yaml
Some checks failed
Continuous Deployment / Update Deployment (push) Failing after 1m32s
debug workflow action
2025-12-09 14:27:42 -06:00
8c1b132f8d Merge branch 'Calendar-Improvements' of https://gitea.iceberg-gaming.com/17th-Ranger-Battalion-ORG/milsim-site-v4 into Calendar-Improvements
Some checks failed
Continuous Deployment / Update Deployment (push) Failing after 1m17s
2025-12-05 16:47:27 -05:00
4e69b419f0 Merge branch 'main' into Calendar-Improvements
Some checks failed
Continuous Deployment / Update Deployment (push) Failing after 1m18s
2025-12-05 15:43:49 -06:00
a233b15ad4 Switched clock icon to something more readable at that size 2025-12-05 16:34:00 -05:00
94b5bfcff7 Added support for public read only calendar #53
Some checks failed
Continuous Deployment / Update Deployment (push) Failing after 1m18s
2025-12-05 16:28:42 -05:00
09d20fd18f prevented header wiggle when editable value changes
Some checks failed
Continuous Deployment / Update Deployment (push) Failing after 1m18s
2025-12-05 16:16:51 -05:00
89280dd3a7 Added (hidden) attendees summary
Some checks failed
Continuous Deployment / Update Deployment (push) Failing after 1m18s
2025-12-05 16:12:25 -05:00
575455a0fc overhauled attendance visualization to be more useful for planning
Some checks failed
Continuous Deployment / Update Deployment (push) Failing after 1m22s
2025-12-05 16:04:32 -05:00
ccb132b9b0 Improvements to event details UI to address #41
Some checks failed
Continuous Deployment / Update Deployment (push) Failing after 1m30s
2025-12-05 12:53:32 -05:00
e550f862bc event details area initial redesign 2025-12-04 23:41:55 -05:00
6d6789c4a6 Disabled attendance buttons if event end time is past current time to address #55
Some checks failed
Continuous Deployment / Update Deployment (push) Failing after 1m13s
2025-12-04 22:55:57 -05:00
10 changed files with 291 additions and 120 deletions

View File

@@ -1,6 +1,8 @@
name: Continuous Deployment
on:
push:
tags:
- '*'
jobs:
Deploy:
@@ -8,20 +10,27 @@ jobs:
runs-on: ubuntu-latest
container:
volumes:
- /var/www/html/milsim-site-v4:/var/www/html/milsim-site-v4:rw
- /var/www/html/milsim-site-v4:/var/www/html/milsim-site-v4:z
steps:
- name: Setup Local Environment
run: |
groupadd -g 989 nginx || true
useradd nginx -u 990 -g nginx -m || true
- name: Update Node Environment
uses: actions/setup-node@v6
with:
node-version: 20.19
- name: Verify Node Environment
run: |
which npm
npm -v
which node
node -v
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
fetch-depth: 0
ref: 'main'
@@ -32,31 +41,43 @@ jobs:
cp /workspace/17th-Ranger-Battalion-ORG/milsim-site-v4/.git/config .git/config
chown nginx:nginx .git/config
- name: Fix File Permissions
- name: Update Application Code
run: |
cd /var/www/html/milsim-site-v4
sudo -u nginx git reset --hard
sudo -u nginx git pull origin main
- name: Update Shared Dependencies and Fix Permissions
run: |
cd /var/www/html/milsim-site-v4/shared
npm install
chown -R nginx:nginx .
- name: Update UI Dependencies and Fix Permissions
run: |
cd /var/www/html/milsim-site-v4/ui
npm install
chown -R nginx:nginx .
- name: Update API Dependencies and Fix Permissions
run: |
cd /var/www/html/milsim-site-v4/api
npm install
chown -R nginx:nginx .
- name: Build UI and Fix Permissions
run: |
cd /var/www/html/milsim-site-v4/ui
npm run build
chown -R nginx:nginx .
- name: Build API and Fix Permissions
run: |
cd /var/www/html/milsim-site-v4/api
npm run build
chown -R nginx:nginx .
- name: Reset 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"

View File

@@ -0,0 +1,83 @@
name: Continuous Integration
on:
push:
branches:
- main
jobs:
Deploy:
name: Update Development
runs-on: ubuntu-latest
container:
volumes:
- /var/www/html/milsim-site-v4-dev:/var/www/html/milsim-site-v4:z
steps:
- name: Setup Local Environment
run: |
groupadd -g 989 nginx || true
useradd nginx -u 990 -g nginx -m || true
- name: Update Node Environment
uses: actions/setup-node@v6
with:
node-version: 20.19
- name: Verify Node Environment
run: |
which npm
npm -v
which node
node -v
- name: Checkout
uses: actions/checkout@v5
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: Update Application Code
run: |
cd /var/www/html/milsim-site-v4
sudo -u nginx git reset --hard
sudo -u nginx git pull origin main
- name: Update Shared Dependencies and Fix Permissions
run: |
cd /var/www/html/milsim-site-v4/shared
npm install
chown -R nginx:nginx .
- name: Update UI Dependencies and Fix Permissions
run: |
cd /var/www/html/milsim-site-v4/ui
npm install
chown -R nginx:nginx .
- name: Update API Dependencies and Fix Permissions
run: |
cd /var/www/html/milsim-site-v4/api
npm install
chown -R nginx:nginx .
- name: Build UI and Fix Permissions
run: |
cd /var/www/html/milsim-site-v4/ui
npm run build
chown -R nginx:nginx .
- name: Build API and Fix Permissions
run: |
cd /var/www/html/milsim-site-v4/api
npm run build
chown -R nginx:nginx .
- name: Reset 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

View File

@@ -123,15 +123,9 @@ export async function setAttendanceStatus(memberID: number, eventID: number, sta
}
export async function getEventAttendance(eventID: number): Promise<CalendarSignup[]> {
const sql = `
SELECT
s.member_id,
s.status,
m.name AS member_name
FROM calendar_events_signups s
LEFT JOIN members m ON s.member_id = m.id
WHERE s.event_id = ?
`;
return await pool.query(sql, [eventID]);
const sql = "CALL `sp_GetCalendarEventSignups`(?)"
const res = await pool.query(sql, [eventID]);
console.log(res[0]);
return res[0];
}

View File

@@ -5,6 +5,7 @@ module.exports = {
script: 'built/api/src/index.js',
watch: ['.env', 'built'],
ignore_watch: ['.gitignore', '\.json', 'src', '\.db', 'node_modules'],
appendEnvToName: true,
watch_options: {
usePolling: true,
interval: 10000

View File

@@ -26,6 +26,7 @@ export interface CalendarSignup {
eventID: number;
status: CalendarAttendance;
member_name?: string;
member_unit?: string;
}
export interface CalendarEventShort {

View File

@@ -156,6 +156,12 @@ function blurAfter() {
<RouterLink to="/join" @click="blurAfter">Join</RouterLink>
</NavigationMenuLink>
</NavigationMenuItem>
<!-- Calendar -->
<NavigationMenuItem>
<NavigationMenuLink as-child :class="navigationMenuTriggerStyle()">
<RouterLink to="/calendar" @click="blurAfter">Calendar</RouterLink>
</NavigationMenuLink>
</NavigationMenuItem>
</NavigationMenuList>
</NavigationMenu>
</div>

View File

@@ -1,7 +1,7 @@
<script setup lang="ts">
import type { CalendarEvent, CalendarSignup } from '@shared/types/calendar'
import { CircleAlert, Clock, EllipsisVertical, MapPin, User, X } from 'lucide-vue-next';
import { computed, onMounted, ref, watch } from 'vue';
import { CircleAlert, Clock4, EllipsisVertical, MapPin, User, X } from 'lucide-vue-next';
import { computed, ref, watch } from 'vue';
import ButtonGroup from '../ui/button-group/ButtonGroup.vue';
import Button from '../ui/button/Button.vue';
import { CalendarAttendance, getCalendarEvent, setCalendarEventAttendance, setCancelCalendarEvent } from '@/api/calendar';
@@ -11,24 +11,13 @@ import DropdownMenu from '../ui/dropdown-menu/DropdownMenu.vue';
import DropdownMenuTrigger from '../ui/dropdown-menu/DropdownMenuTrigger.vue';
import DropdownMenuContent from '../ui/dropdown-menu/DropdownMenuContent.vue';
import DropdownMenuItem from '../ui/dropdown-menu/DropdownMenuItem.vue';
import { Calendar } from 'lucide-vue-next';
const route = useRoute();
// const eventID = computed(() => {
// const id = route.params.id;
// if (typeof id === 'string') return id;
// return undefined;
// });
const loaded = ref<boolean>(false);
const activeEvent = ref<CalendarEvent | null>(null);
// onMounted(async () => {
// let eventID = route.params.id;
// console.log(eventID);
// activeEvent.value = await getCalendarEvent(Number(eventID));
// loaded.value = true;
// });
watch(
() => route.params.id,
async (id) => {
@@ -45,23 +34,27 @@ const emit = defineEmits<{
(e: 'edit', event: CalendarEvent): void
}>()
// const activeEvent = computed(() => props.event)
const startFmt = new Intl.DateTimeFormat(undefined, {
weekday: 'short', year: 'numeric', month: 'short', day: 'numeric',
hour: 'numeric', minute: '2-digit'
const dateFmt = new Intl.DateTimeFormat(undefined, {
weekday: 'long', month: 'short', day: 'numeric',
})
const endFmt = new Intl.DateTimeFormat(undefined, {
const timeFmt = new Intl.DateTimeFormat(undefined, {
hour: 'numeric', minute: '2-digit'
})
const whenText = computed(() => {
if (!activeEvent.value?.start) return ''
const s = new Date(activeEvent.value.start)
const e = activeEvent.value?.end ? new Date(activeEvent.value.end) : null
return e
? `${startFmt.format(s)} ${endFmt.format(e)}`
: `${startFmt.format(s)}`
const dateText = computed(() => {
let start = dateFmt.format(new Date(activeEvent.value.start));
let end = dateFmt.format(new Date(activeEvent.value.end));
if (start === end)
return start;
else
return `${start} - ${end}`;
})
const timeText = computed(() => {
let startTime = timeFmt.format(new Date(activeEvent.value.start))
let endTime = timeFmt.format(new Date(activeEvent.value.end))
return [startTime, endTime]
})
const attending = computed<CalendarSignup[]>(() => { return activeEvent.value.eventSignups.filter((s) => s.status == CalendarAttendance.Attending) })
@@ -71,6 +64,7 @@ const viewedState = ref<CalendarAttendance>(CalendarAttendance.Attending);
let user = useUserStore();
const myAttendance = computed<CalendarSignup | null>(() => {
if (!user.isLoggedIn) return null;
return activeEvent.value.eventSignups.find(
(s) => s.member_id === user.user.id
) || null;
@@ -83,6 +77,7 @@ async function setAttendance(state: CalendarAttendance) {
}
const canEditEvent = computed(() => {
if (!user.isLoggedIn) return false;
if (user.user.id == activeEvent.value.creator_id)
return true;
});
@@ -97,17 +92,94 @@ async function forceReload() {
activeEvent.value = await getCalendarEvent(activeEvent.value.id);
}
defineExpose({forceReload})
const isPast = computed(() => {
const end = new Date(activeEvent.value.end)
// is current date later than end date
return new Date() < end;
})
const attendanceTab = ref<"Alpha" | "Echo" | "Other">("Alpha");
const attendanceList = computed<CalendarSignup[]>(() => {
let out: CalendarSignup[] = [];
if (attendanceTab.value === 'Alpha') {
out = activeEvent.value.eventSignups?.filter((s) => s.member_unit === 'Alpha Company');
} else if (attendanceTab.value === 'Echo') {
out = activeEvent.value.eventSignups?.filter((s) => s.member_unit === 'Echo Company')
} else {
out = activeEvent.value.eventSignups?.filter((s) => s.member_unit != 'Alpha Company' && s.member_unit != 'Echo Company')
}
const statusOrder: Record<CalendarAttendance, number> = {
[CalendarAttendance.Attending]: 1,
[CalendarAttendance.Maybe]: 2,
[CalendarAttendance.NotAttending]: 3,
};
out.sort((a, b) => statusOrder[a.status] - statusOrder[b.status]);
return out;
})
const attendanceCountsByGroup = computed(() => {
const signups = activeEvent.value.eventSignups ?? [];
return {
Alpha: signups.filter(s => s.member_unit === "Alpha Company").length,
Echo: signups.filter(s => s.member_unit === "Echo Company").length,
Other: signups.filter(s =>
s.member_unit !== "Alpha Company" &&
s.member_unit !== "Echo Company"
).length,
};
});
const attendanceStatusSummary = computed(() => {
const signups = activeEvent.value.eventSignups ?? [];
return {
attending: signups.filter(s => s.status === CalendarAttendance.Attending).length,
maybe: signups.filter(s => s.status === CalendarAttendance.Maybe).length,
notAttending: signups.filter(s => s.status === CalendarAttendance.NotAttending).length,
};
});
const statusColor = (status: CalendarAttendance) => {
switch (status) {
case CalendarAttendance.Attending:
return "text-success";
case CalendarAttendance.Maybe:
return "text-yellow-600";
case CalendarAttendance.NotAttending:
return "text-destructive";
default:
return "";
}
};
const displayStatus = (status: CalendarAttendance) => {
switch (status) {
case CalendarAttendance.Attending:
return "Attending";
case CalendarAttendance.Maybe:
return "Maybe";
case CalendarAttendance.NotAttending:
return "Declined";
default:
return status;
}
};
defineExpose({ forceReload })
</script>
<template>
<div v-if="loaded">
<!-- Header -->
<div class="flex items-center justify-between gap-3 border-b px-4 py-3">
<div class="flex items-center justify-between gap-3 border-b px-4 py-3 h-14">
<h2 class="text-lg font-semibold break-all">
{{ activeEvent?.name || 'Event' }}
</h2>
<div class="flex gap-4">
<div class="flex gap-4 items-center">
<DropdownMenu v-if="canEditEvent">
<DropdownMenuTrigger>
<button
@@ -119,8 +191,7 @@ defineExpose({forceReload})
<DropdownMenuItem @click="emit('edit', activeEvent)">
Edit
</DropdownMenuItem>
<DropdownMenuItem v-if="activeEvent.cancelled"
@click="setCancel(false)">
<DropdownMenuItem v-if="activeEvent.cancelled" @click="setCancel(false)">
Un-Cancel
</DropdownMenuItem>
<DropdownMenuItem v-else @click="setCancel(true)">
@@ -142,7 +213,7 @@ defineExpose({forceReload})
<CircleAlert></CircleAlert> This event has been cancelled
</div>
</section>
<section class="w-full">
<section v-if="isPast && user.isLoggedIn" class="w-full">
<ButtonGroup class="flex w-full">
<Button variant="outline"
:class="myAttendance?.status === CalendarAttendance.Attending ? 'border-2 border-primary text-primary' : ''"
@@ -155,24 +226,21 @@ defineExpose({forceReload})
@click="setAttendance(CalendarAttendance.NotAttending)">Declined</Button>
</ButtonGroup>
</section>
<!-- When -->
<section v-if="whenText" class="space-y-2 w-full">
<div class="inline-flex items-center gap-2 rounded-md border px-2.5 py-1.5 text-sm">
<Clock class="size-4 opacity-80" />
<span class="font-medium">{{ whenText }}</span>
<!-- Meta -->
<section class="space-y-3 w-full">
<p class="text-lg font-semibold">Details</p>
<div class="text-foreground/80 flex gap-3 items-center">
<Calendar :size="20"></Calendar> {{ dateText }}
</div>
<div class="text-foreground/80 flex gap-3 items-center">
<Clock4 :size="20"></Clock4> {{ timeText[0] }} - {{ timeText[1] }}
</div>
<div class="text-foreground/80 flex gap-3 items-center">
<MapPin :size="20"></MapPin> {{ activeEvent.location || "Unknown" }}
</div>
<div class="text-foreground/80 flex gap-3 items-center">
<User :size="20"></User> {{ activeEvent.creator_name || "Unknown User" }}
</div>
</section>
<!-- Quick meta chips -->
<section class="flex flex-wrap gap-2 w-full">
<span class="inline-flex items-center gap-1.5 rounded-md border px-2 py-1 text-xs">
<MapPin class="size-3.5 opacity-80" />
<span class="font-medium">{{ activeEvent.location || "Unknown" }}</span>
</span>
<span class="inline-flex items-center gap-1.5 rounded-md border px-2 py-1 text-xs">
<User class="size-3.5 opacity-80" />
<span class="font-medium">Created by: {{ activeEvent.creator_name || "Unknown User"
}}</span>
</span>
</section>
<!-- Description -->
<section class="space-y-2 w-full">
@@ -181,46 +249,41 @@ defineExpose({forceReload})
{{ activeEvent.description }}
</p>
</section>
<!-- Attendance -->
<!-- attendance -->
<section class="space-y-2 w-full">
<div class="flex items-center gap-5">
<p class="text-lg font-semibold">Attendance</p>
<!-- <div class="text-muted-foreground flex gap-6">
<p>Going <span class="ml-1">{{ attendanceStatusSummary.attending }}</span></p>
<p>Maybe <span class="ml-1">{{ attendanceStatusSummary.maybe }}</span></p>
<p>Declined <span class="ml-1">{{ attendanceStatusSummary.notAttending }}</span></p>
</div> -->
</div>
<div class="flex flex-col border bg-muted/50 rounded-lg min-h-24 my-2">
<div class="flex w-full pt-2 border-b *:w-full *:text-center *:pb-1 *:cursor-pointer">
<label
:class="viewedState === CalendarAttendance.Attending ? 'border-b-3 border-foreground' : 'mb-[2px]'"
@click="viewedState = CalendarAttendance.Attending">Going {{ attending.length }}</label>
<label
:class="viewedState === CalendarAttendance.Maybe ? 'border-b-3 border-foreground' : 'mb-[2px]'"
@click="viewedState = CalendarAttendance.Maybe">Maybe {{ maybe.length }}</label>
<label
:class="viewedState === CalendarAttendance.NotAttending ? 'border-b-3 border-foreground' : 'mb-[2px]'"
@click="viewedState = CalendarAttendance.NotAttending">Declined {{ declined.length
}}</label>
<label :class="attendanceTab === 'Alpha' ? 'border-b-3 border-foreground' : 'mb-[2px]'"
@click="attendanceTab = 'Alpha'">Alpha {{ attendanceCountsByGroup.Alpha }}</label>
<label :class="attendanceTab === 'Echo' ? 'border-b-3 border-foreground' : 'mb-[2px]'"
@click="attendanceTab = 'Echo'">Echo {{ attendanceCountsByGroup.Echo }}</label>
<label :class="attendanceTab === 'Other' ? 'border-b-3 border-foreground' : 'mb-[2px]'"
@click="attendanceTab = 'Other'">Other {{ attendanceCountsByGroup.Other }}</label>
</div>
<div class="px-5 py-4 min-h-28">
<div v-if="viewedState === CalendarAttendance.Attending" v-for="person in attending">
<p>{{ person.member_name }}</p>
<div class="pb-1 min-h-48">
<div class="grid grid-cols-2 font-semibold text-muted-foreground border-b py-1 px-3 mb-2">
<p>Name</p>
<p class="text-right">Status</p>
</div>
<div v-if="viewedState === CalendarAttendance.Maybe" v-for="person in maybe">
<p>{{ person.member_name }}</p>
</div>
<div v-if="viewedState === CalendarAttendance.NotAttending" v-for="person in declined">
<div v-for="person in attendanceList" :key="person.member_id"
class="grid grid-cols-2 py-1 *:px-3 hover:bg-muted">
<p>{{ person.member_name }}</p>
<p :class="statusColor(person.status)" class="text-right">
{{ displayStatus(person.status) }}
</p>
</div>
</div>
</div>
</section>
</div>
<!-- Footer (optional actions) -->
<!-- <div class="border-t px-4 py-3 flex items-center justify-end gap-2">
<button class="rounded-md border px-3 py-1.5 text-sm hover:bg-muted/40 transition">
Edit
</button>
<button
class="rounded-md bg-primary text-primary-foreground px-3 py-1.5 text-sm hover:opacity-90 transition">
Open details
</button>
</div> -->
</div>
</template>

View File

@@ -3,15 +3,14 @@ import { ref, watch, nextTick, computed, onMounted } from 'vue'
import FullCalendar from '@fullcalendar/vue3'
import dayGridPlugin from '@fullcalendar/daygrid'
import interactionPlugin from '@fullcalendar/interaction'
import { X, Clock, MapPin, User, ListTodo, ChevronLeft, ChevronRight, Plus } from 'lucide-vue-next'
import { ChevronLeft, ChevronRight, Plus } from 'lucide-vue-next'
import CreateCalendarEvent from '@/components/calendar/CreateCalendarEvent.vue'
import { getCalendarEvent, getMonthCalendarEvents } from '@/api/calendar'
import { CalendarEvent, CalendarEventShort } from '@shared/types/calendar'
import { Calendar } from '@fullcalendar/core'
import { CalendarEvent } from '@shared/types/calendar'
import ViewCalendarEvent from '@/components/calendar/ViewCalendarEvent.vue'
import { useRouter, useRoute } from 'vue-router'
import { useCalendarEvents } from '@/composables/useCalendarEvents'
import { useCalendarNavigation } from '@/composables/useCalendarNavigation'
import { useUserStore } from '@/stores/user'
const monthLabels = [
'January', 'February', 'March', 'April', 'May', 'June',
@@ -24,13 +23,14 @@ function api() {
const router = useRouter();
const route = useRoute();
const userStore = useUserStore();
function buildFullDate(month: number, year: number): Date {
return new Date(year, month, 1); //default to first of month
}
const { selectedMonth, selectedYear, years, goPrev, goNext, goToday, onDatesSet, goToSelectedDate } = useCalendarNavigation(api)
const { events, loadEvents} = useCalendarEvents(selectedMonth, selectedYear);
const { events, loadEvents } = useCalendarEvents(selectedMonth, selectedYear);
const panelOpen = ref(false)
const activeEvent = ref<CalendarEvent | null>(null)
@@ -48,6 +48,7 @@ const dialogRef = ref<any>(null)
// NEW: handle day/time slot clicks to start creating an event
function onDateClick(arg: { dateStr: string }) {
if (!userStore.isLoggedIn) return;
dialogRef.value?.openDialog(arg.dateStr);
// For now, just open the panel with a draft payload.
// activeEvent.value = {
@@ -202,7 +203,7 @@ onMounted(() => {
@click="goToday">
Today
</button>
<button
<button v-if="userStore.isLoggedIn"
class="cursor-pointer ml-1 inline-flex items-center gap-1.5 rounded-md bg-primary px-3 py-1.5 text-sm text-primary-foreground hover:opacity-90"
@click="onCreateEvent">
<Plus class="h-4 w-4" />
@@ -216,7 +217,8 @@ onMounted(() => {
<aside v-if="panelOpen"
class="3xl:w-lg 2xl:w-md border-l bg-card text-foreground flex flex-col overflow-auto scrollbar-themed"
:style="{ height: 'calc(100vh - 61px)', position: 'sticky', top: '64px' }">
<ViewCalendarEvent ref="eventViewRef" @close="() => { router.push('/calendar'); }" @reload="loadEvents()" @edit="(val) => {dialogRef.openDialog(null, 'edit', val)}">
<ViewCalendarEvent ref="eventViewRef" @close="() => { router.push('/calendar'); }"
@reload="loadEvents()" @edit="(val) => { dialogRef.openDialog(null, 'edit', val) }">
</ViewCalendarEvent>
</aside>
</div>

View File

@@ -171,9 +171,9 @@ const finalPanel = ref<'app' | 'message'>('message');
<li>When prompted, choose <em>“Yes”</em> to download all associated mods.</li>
</ul>
<p>
<a href="https://docs.iceberg-gaming.com/books/member-guides/page/new-member-setup-onboarding"
<a href="https://www.guilded.gg/Iceberg-gaming/groups/v3j2vAP3/channels/6979335e-60f7-4ab9-9590-66df69367d1e/docs/2013948655"
class="text-primary underline" target="_blank">
Click here for the full installation guide (Requires Sign-in)
Click here for the full installation guide
</a>
</p>
<!-- CONTACT SECTION -->
@@ -211,7 +211,7 @@ const finalPanel = ref<'app' | 'message'>('message');
our forums and introduce yourself.
</p>
<p>
If you have any questions, feel free to reach out on TeamSpeak or Discord
If you have any questions, feel free to reach out on TeamSpeak, Discord, or Guilded,
someone
will always be around to help.
</p>

View File

@@ -16,8 +16,8 @@ const router = createRouter({
{ path: '/loa', component: () => import('@/pages/SubmitLOA.vue'), meta: { requiresAuth: true, memberOnly: true } },
{ path: '/transfer', component: () => import('@/pages/Transfer.vue'), meta: { requiresAuth: true, memberOnly: true } },
{ path: '/calendar', component: () => import('@/pages/Calendar.vue'), meta: { requiresAuth: true, memberOnly: true }, },
{ path: '/calendar/event/:id', component: () => import('@/pages/Calendar.vue'), meta: { requiresAuth: true, memberOnly: true }, },
{ path: '/calendar', component: () => import('@/pages/Calendar.vue'), meta: { }, },
{ path: '/calendar/event/:id', component: () => import('@/pages/Calendar.vue'), meta: { }, },
{ path: '/trainingReport', component: () => import('@/pages/TrainingReport.vue'), meta: { requiresAuth: true, memberOnly: true } },
{ path: '/trainingReport/new', component: () => import('@/pages/TrainingReport.vue'), meta: { requiresAuth: true, memberOnly: true } },