Merge remote-tracking branch 'Origin/main' into promotions
This commit is contained in:
@@ -49,15 +49,8 @@ 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;
|
||||
if (userStore.state !== 'member') return;
|
||||
dialogRef.value?.openDialog(arg.dateStr);
|
||||
// For now, just open the panel with a draft payload.
|
||||
// activeEvent.value = {
|
||||
// id: '__draft__',
|
||||
// title: 'New event',
|
||||
// start: arg.dateStr,
|
||||
// extendedProps: { draft: true }
|
||||
// }
|
||||
// panelOpen.value = true
|
||||
}
|
||||
|
||||
const calendarOptions = ref({
|
||||
@@ -203,7 +196,7 @@ onMounted(() => {
|
||||
@click="goToday">
|
||||
Today
|
||||
</button>
|
||||
<button v-if="userStore.isLoggedIn"
|
||||
<button v-if="userStore.isLoggedIn && userStore.state === 'member'"
|
||||
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" />
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import { getWelcomeMessage } from '@/api/docs';
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
const router = useRouter()
|
||||
@@ -8,23 +10,126 @@ const router = useRouter()
|
||||
const user = useUserStore();
|
||||
|
||||
function goToApplication() {
|
||||
router.push('/apply') // change to your form route
|
||||
router.push('/join') // change to your form route
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
if (user.state == 'member') {
|
||||
let policy = await getWelcomeMessage() as any;
|
||||
welcomeRef.value.innerHTML = policy;
|
||||
}
|
||||
})
|
||||
|
||||
const welcomeRef = ref<HTMLElement>(null);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div v-if="user.state == 'guest'" class="flex flex-col items-center justify-center">
|
||||
<h1 class="text-4xl font-bold mb-4">Welcome to the 17th</h1>
|
||||
<p class="text-neutral-400 mb-8 max-w-md">
|
||||
To join our unit, please fill out an application to continue.
|
||||
</p>
|
||||
<Button @click="goToApplication" class="px-6 py-3 text-lg">
|
||||
Begin Application
|
||||
</Button>
|
||||
<div v-if="user.state == 'member'" class="mt-10">
|
||||
<div ref="welcomeRef" class="bookstack-container">
|
||||
<!-- bookstack -->
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
HOMEPAGEEEEEEEEEEEEEEEEEEE
|
||||
<div v-else class="text-foreground px-6 py-12 selection:bg-primary/10">
|
||||
<div class="max-w-5xl mx-auto space-y-8">
|
||||
|
||||
<header class="space-y-4">
|
||||
<div class="flex flex-col md:flex-row md:items-end justify-between gap-4">
|
||||
<div>
|
||||
<h1 class="text-4xl font-semibold tracking-tight">17th Ranger Battalion</h1>
|
||||
</div>
|
||||
</div>
|
||||
<div class="h-px bg-border w-full"></div>
|
||||
</header>
|
||||
|
||||
<div class="grid grid-cols-1 lg:grid-cols-12 gap-12">
|
||||
|
||||
<div class="lg:col-span-7 space-y-6">
|
||||
<div class="space-y-2">
|
||||
<h2 class="text-sm font-medium uppercase tracking-wider text-primary">Unit Philosophy</h2>
|
||||
<p class="text-lg leading-relaxed font-normal">
|
||||
The 17th RBN emphasizes high-skill gameplay through real-world tactics, stripped of
|
||||
traditional military formalities. We prioritize effective coordination over enforced
|
||||
etiquette.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<p class="text-muted-foreground leading-relaxed">
|
||||
Our "Real Life First" mindset ensures participation remains a hobby, not a second job. With
|
||||
a consistent roster of 40–50 members for Saturday operations, we focus on effective
|
||||
coordination and mission success without the requirement of "Yes sir, no sir" protocols.
|
||||
</p>
|
||||
|
||||
<div class="flex flex-wrap items-center gap-6 pt-4">
|
||||
<Button size="lg" @click="goToApplication" class="font-medium">
|
||||
Begin Application
|
||||
</Button>
|
||||
<div class="flex flex-col">
|
||||
<span class="text-xs uppercase tracking-tighter text-muted-foreground">Age
|
||||
Requirement</span>
|
||||
<span class="text-sm font-medium">18+</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="lg:col-span-5 space-y-8">
|
||||
<section class="space-y-3">
|
||||
<h3 class="text-xs font-semibold uppercase tracking-widest text-muted-foreground">
|
||||
Operational Schedule</h3>
|
||||
<div class="rounded-lg border bg-card p-4 shadow-sm">
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-sm font-medium">Main Operation</span>
|
||||
<span class="text-sm font-mono text-primary">Sat 19:00 CST</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="space-y-4">
|
||||
<h3 class="text-xs font-semibold uppercase tracking-[0.2em] text-muted-foreground">
|
||||
Force Structure
|
||||
</h3>
|
||||
<ul class="space-y-3">
|
||||
<li class="flex items-start gap-4">
|
||||
<span class="h-1.5 w-1.5 rounded-full bg-primary mt-2 shrink-0"></span>
|
||||
<div class="space-y-1">
|
||||
<p class="text-sm font-medium leading-none">Alpha Company</p>
|
||||
<p class="text-[13px] text-muted-foreground leading-relaxed">
|
||||
Rifleman, Medic, CLS, Anti-Tank, RTO, Leadership
|
||||
</p>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="flex items-start gap-4">
|
||||
<span class="h-1.5 w-1.5 rounded-full bg-primary mt-2 shrink-0"></span>
|
||||
<div class="space-y-1">
|
||||
<p class="text-sm font-medium leading-none">Echo Company</p>
|
||||
<p class="text-[13px] text-muted-foreground leading-relaxed">
|
||||
Logistics, CAS Pilot, Armor, Artillery, JTAC, Forward Observer
|
||||
</p>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p class="text-[12px] text-muted-foreground/70 border-t pt-2 border-border">
|
||||
Roles are fluid; specialization or weekly rotation is supported.
|
||||
</p>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<section class="space-y-4">
|
||||
<div class="relative rounded-xl border bg-card shadow-sm overflow-hidden">
|
||||
<div class="aspect-video">
|
||||
<iframe class="w-full h-full"
|
||||
src="https://www.youtube.com/embed/61L397HwmrU?si=oY9qf6vFv6hXo6Fk&controls=1&mute=1&start=102&end=152"
|
||||
title="YouTube video player" frameborder="0"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
|
||||
allowfullscreen>
|
||||
</iframe>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -34,7 +34,8 @@ import { Plus, X } from 'lucide-vue-next';
|
||||
import Separator from '@/components/ui/separator/Separator.vue';
|
||||
import Input from '@/components/ui/input/Input.vue';
|
||||
import Label from '@/components/ui/label/Label.vue';
|
||||
import { getMembers, Member } from '@/api/member';
|
||||
import { getMembers } from '@/api/member';
|
||||
import { Member } from '@shared/types/member';
|
||||
|
||||
const roles = ref<Role[]>([])
|
||||
const activeRole = ref<Role | null>(null)
|
||||
@@ -116,16 +117,18 @@ async function handleCreateGroup() {
|
||||
}
|
||||
}
|
||||
|
||||
function handleAddMember() {
|
||||
async function handleAddMember() {
|
||||
//guard
|
||||
if (memberToAdd.value == null)
|
||||
return;
|
||||
|
||||
addMemberToRole(memberToAdd.value.member_id, activeRole.value.id);
|
||||
await addMemberToRole(memberToAdd.value.member_id, activeRole.value.id);
|
||||
roles.value = await getRoles();
|
||||
}
|
||||
|
||||
function handleRemoveMember(memberId: number) {
|
||||
async function handleRemoveMember(memberId: number) {
|
||||
removeMemberFromRole(memberId, activeRole.value.id);
|
||||
roles.value = await getRoles();
|
||||
}
|
||||
|
||||
async function handleDeleteRole() {
|
||||
@@ -193,7 +196,7 @@ onMounted(async () => {
|
||||
</ul>
|
||||
</div>
|
||||
<DialogFooter>
|
||||
<Button @click="handleDeleteRole">Delete Group</Button>
|
||||
<!-- <Button @click="handleDeleteRole">Delete Group</Button> -->
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
@@ -232,7 +235,7 @@ onMounted(async () => {
|
||||
<div class="max-w-5xl mx-auto">
|
||||
<div class="flex items-center justify-between my-4">
|
||||
<p>Groups</p>
|
||||
<Button @click="showCreateGroupDialog = true">+ Add New Group</Button>
|
||||
<!-- <Button @click="showCreateGroupDialog = true">+ Add New Group</Button> -->
|
||||
</div>
|
||||
<div class="grid grid-cols-3 gap-5">
|
||||
<Card v-for="value in roles" :key="value.id" @click="activeRole = value; showDialog = true"
|
||||
|
||||
@@ -11,6 +11,7 @@ import { useMemberDirectory } from "@/stores/memberDirectory";
|
||||
import { useUserStore } from "@/stores/user";
|
||||
|
||||
const saving = ref(false);
|
||||
const saveSuccess = ref(false);
|
||||
const loading = ref(true);
|
||||
const showLoading = ref(false);
|
||||
const form = ref<memberSettings>();
|
||||
@@ -20,13 +21,14 @@ const userStore = useUserStore()
|
||||
|
||||
function saveSettings() {
|
||||
saving.value = true;
|
||||
saveSuccess.value = false;
|
||||
|
||||
setTimeout(async () => {
|
||||
// Replace with your API save call
|
||||
setMemberSettings(form.value);
|
||||
saving.value = false;
|
||||
console.log(userStore.user.id)
|
||||
memberDictionary.invalidateMember(userStore.user.id)
|
||||
memberDictionary.invalidateMember(userStore.user.member.member_id)
|
||||
saveSuccess.value = true;
|
||||
}, 800);
|
||||
}
|
||||
|
||||
@@ -75,7 +77,8 @@ onMounted(async () => {
|
||||
</CardContent>
|
||||
</Transition>
|
||||
|
||||
<CardFooter class="flex justify-end">
|
||||
<CardFooter class="flex justify-end gap-5 items-center">
|
||||
<p v-if="saveSuccess" class="text-green-500">Profile saved</p>
|
||||
<Button @click="saveSettings" :disabled="saving">
|
||||
{{ saving ? "Saving..." : "Save Changes" }}
|
||||
</Button>
|
||||
|
||||
Reference in New Issue
Block a user