added support for posting an LOA
This commit is contained in:
45
ui/src/api/loa.ts
Normal file
45
ui/src/api/loa.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
export type LOARequest = {
|
||||
member_id: number;
|
||||
filed_date: string; // ISO 8601 string
|
||||
start_date: string; // ISO 8601 string
|
||||
end_date: string; // ISO 8601 string
|
||||
reason?: string;
|
||||
};
|
||||
|
||||
const addr = "localhost:3000";
|
||||
|
||||
export async function submitLOA(request: LOARequest): Promise<{ id?: number; error?: string }> {
|
||||
const res = await fetch(`http://${addr}/loa`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(request),
|
||||
});
|
||||
|
||||
if (res.ok) {
|
||||
return res.json();
|
||||
} else {
|
||||
return { error: "Failed to submit LOA" };
|
||||
}
|
||||
}
|
||||
|
||||
export async function getMyLOA(): Promise<LOARequest | null> {
|
||||
const res = await fetch(`http://${addr}/loa/me`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
if (res.ok) {
|
||||
const out = res.json();
|
||||
if (!out) {
|
||||
return null;
|
||||
}
|
||||
return out;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { Check, Search } from "lucide-vue-next"
|
||||
import { Combobox, ComboboxAnchor, ComboboxEmpty, ComboboxGroup, ComboboxInput, ComboboxItem, ComboboxItemIndicator, ComboboxList } from "@/components/ui/combobox"
|
||||
|
||||
import { getRanks, Rank } from "@/api/rank"
|
||||
import { onMounted, ref } from "vue";
|
||||
import { Member, getMembers } from "@/api/member";
|
||||
@@ -22,10 +21,9 @@ import { CalendarIcon } from "lucide-vue-next"
|
||||
import Input from "@/components/ui/input/Input.vue";
|
||||
import Textarea from "@/components/ui/textarea/Textarea.vue";
|
||||
import Separator from "@/components/ui/separator/Separator.vue";
|
||||
|
||||
import { submitLOA } from "@/api/loa"; // <-- import the submit function
|
||||
|
||||
const members = ref<Member[]>([])
|
||||
|
||||
const currentMember = ref<Member | null>(null);
|
||||
|
||||
defineProps({
|
||||
@@ -33,7 +31,6 @@ defineProps({
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
const df = new DateFormatter("en-US", {
|
||||
@@ -45,10 +42,57 @@ const value = ref({
|
||||
end: new CalendarDate(2022, 1, 20).add({ days: 20 }),
|
||||
}) as Ref<DateRange>
|
||||
|
||||
const reason = ref(""); // <-- reason for LOA
|
||||
const submitting = ref(false);
|
||||
const submitError = ref<string | null>(null);
|
||||
const submitSuccess = ref(false);
|
||||
|
||||
onMounted(async () => {
|
||||
members.value = await getMembers();
|
||||
});
|
||||
|
||||
// Submit handler
|
||||
async function handleSubmit() {
|
||||
submitError.value = null;
|
||||
submitSuccess.value = false;
|
||||
submitting.value = true;
|
||||
|
||||
// Use currentMember if adminMode, otherwise use your own member id (stubbed as 89 here)
|
||||
const member_id = currentMember.value?.member_id ?? 89;
|
||||
|
||||
// Format dates as ISO strings
|
||||
const filed_date = toMariaDBDatetime(new Date());
|
||||
const start_date = toMariaDBDatetime(value.value.start?.toDate(getLocalTimeZone()));
|
||||
const end_date = toMariaDBDatetime(value.value.end?.toDate(getLocalTimeZone()));
|
||||
|
||||
if (!member_id || !filed_date || !start_date || !end_date) {
|
||||
submitError.value = "Missing required fields";
|
||||
submitting.value = false;
|
||||
return;
|
||||
}
|
||||
|
||||
const req = {
|
||||
member_id,
|
||||
filed_date,
|
||||
start_date,
|
||||
end_date,
|
||||
reason: reason.value,
|
||||
};
|
||||
|
||||
const result = await submitLOA(req);
|
||||
submitting.value = false;
|
||||
|
||||
if (result.id) {
|
||||
submitSuccess.value = true;
|
||||
reason.value = "";
|
||||
} else {
|
||||
submitError.value = result.error || "Failed to submit LOA";
|
||||
}
|
||||
}
|
||||
|
||||
function toMariaDBDatetime(date: Date): string {
|
||||
return date.toISOString().slice(0, 19).replace('T', ' ');
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -62,7 +106,6 @@ onMounted(async () => {
|
||||
Policy goes here.
|
||||
</p>
|
||||
</div>
|
||||
<Switch />
|
||||
</div>
|
||||
<div class="flex-1 flex flex-col gap-5">
|
||||
<div class="flex w-full gap-5 ">
|
||||
@@ -113,10 +156,16 @@ onMounted(async () => {
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</div>
|
||||
<Textarea placeholder="Reason for LOA" class="w-full resize-none" />
|
||||
<Textarea
|
||||
v-model="reason"
|
||||
placeholder="Reason for LOA"
|
||||
class="w-full resize-none"
|
||||
/>
|
||||
<div class="flex justify-end">
|
||||
<Button :onClick="() => { }" class="w-min">Submit</Button>
|
||||
<Button :onClick="handleSubmit" :disabled="submitting" class="w-min">Submit</Button>
|
||||
</div>
|
||||
<div v-if="submitError" class="text-red-500 text-sm mt-2">{{ submitError }}</div>
|
||||
<div v-if="submitSuccess" class="text-green-500 text-sm mt-2">LOA submitted successfully!</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
Reference in New Issue
Block a user