241 lines
12 KiB
Vue
241 lines
12 KiB
Vue
<script setup lang="ts">
|
|
import { ModRequestSchema } from '@shared/schemas/modRequest'
|
|
import { useForm, Field as VeeField } from 'vee-validate'
|
|
import { toTypedSchema } from '@vee-validate/zod'
|
|
import { ref } from 'vue'
|
|
import Button from '@/components/ui/button/Button.vue'
|
|
import Field from '@/components/ui/field/Field.vue'
|
|
import FieldLabel from '@/components/ui/field/FieldLabel.vue'
|
|
import FieldError from '@/components/ui/field/FieldError.vue'
|
|
import FieldDescription from '@/components/ui/field/FieldDescription.vue'
|
|
import Input from '@/components/ui/input/Input.vue'
|
|
import Textarea from '@/components/ui/textarea/Textarea.vue'
|
|
import Checkbox from '@/components/ui/checkbox/Checkbox.vue'
|
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
|
import { Separator } from '@/components/ui/separator'
|
|
import { X } from 'lucide-vue-next'
|
|
import { postModRequest } from '@/api/modRequests'
|
|
|
|
const { handleSubmit, resetForm, errors } = useForm({
|
|
validationSchema: toTypedSchema(ModRequestSchema),
|
|
validateOnMount: false,
|
|
initialValues: {
|
|
mod_title: '',
|
|
description: '',
|
|
mod_link: '',
|
|
confirmed_tested: false,
|
|
reason: '',
|
|
detrimental_effects: '',
|
|
keybind_conflicts: '',
|
|
special_considerations: '',
|
|
},
|
|
})
|
|
|
|
const submitting = ref(false)
|
|
const emit = defineEmits(['submit', 'close'])
|
|
|
|
const submitForm = handleSubmit(async (values) => {
|
|
if (submitting.value) return
|
|
submitting.value = true
|
|
try {
|
|
await postModRequest(values);
|
|
emit('submit', values)
|
|
} catch (err) {
|
|
console.error('Error submitting mod request:', err)
|
|
} finally {
|
|
submitting.value = false
|
|
}
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<Card class="border-0 shadow-sm">
|
|
<CardHeader>
|
|
<div class="flex items-start justify-between">
|
|
<div class="flex-1">
|
|
<CardTitle>New Mod Request</CardTitle>
|
|
<CardDescription>Share details about the mod you'd like to see added to our server</CardDescription>
|
|
</div>
|
|
<Button type="button" variant="ghost" @click="emit('close')"
|
|
class="text-muted-foreground hover:text-foreground -mt-1 -mr-2">
|
|
Back to posts <X></X>
|
|
</Button>
|
|
</div>
|
|
</CardHeader>
|
|
|
|
<Separator class="mb-0" />
|
|
|
|
<CardContent>
|
|
<form @submit.prevent="submitForm" class="space-y-8">
|
|
<!-- SECTION: Basic Mod Information -->
|
|
<div class="space-y-5">
|
|
<div class="space-y-1">
|
|
<h3 class="font-semibold text-sm text-foreground">Mod Information</h3>
|
|
<p class="text-xs text-muted-foreground">Core details about the mod</p>
|
|
</div>
|
|
|
|
<div class="space-y-5">
|
|
<!-- Title -->
|
|
<VeeField name="mod_title" v-slot="{ field, errors: e }">
|
|
<Field :data-invalid="!!e.length">
|
|
<FieldLabel class="text-sm font-medium">Mod Title</FieldLabel>
|
|
<Input v-bind="field" placeholder="Name of the mod" rows="4" class="resize-none mt-2" />
|
|
<div class="h-4">
|
|
<FieldError v-if="e.length" :errors="e" />
|
|
</div>
|
|
</Field>
|
|
</VeeField>
|
|
|
|
<!-- Description -->
|
|
<VeeField name="description" v-slot="{ field, errors: e }">
|
|
<Field :data-invalid="!!e.length">
|
|
<FieldLabel class="text-sm font-medium">What is this mod?</FieldLabel>
|
|
<FieldDescription class="text-xs">Brief overview of the mod and its main functionality
|
|
</FieldDescription>
|
|
<Textarea v-bind="field" placeholder="Describe what this mod does..." rows="4"
|
|
class="resize-none mt-2" />
|
|
<div class="h-4">
|
|
<FieldError v-if="e.length" :errors="e" />
|
|
</div>
|
|
</Field>
|
|
</VeeField>
|
|
|
|
<!-- Mod Link -->
|
|
<VeeField name="mod_link" v-slot="{ field, errors: e }">
|
|
<Field :data-invalid="!!e.length">
|
|
<FieldLabel class="text-sm font-medium">Mod Link</FieldLabel>
|
|
<FieldDescription class="text-xs">Where can this mod be found?</FieldDescription>
|
|
<Input v-bind="field" placeholder="https://..." class="mt-2" />
|
|
<div class="h-4">
|
|
<FieldError v-if="e.length" :errors="e" />
|
|
</div>
|
|
</Field>
|
|
</VeeField>
|
|
|
|
<!-- Reason -->
|
|
<VeeField name="reason" v-slot="{ field, errors: e }">
|
|
<Field :data-invalid="!!e.length">
|
|
<FieldLabel class="text-sm font-medium">Why add this mod?</FieldLabel>
|
|
<FieldDescription class="text-xs">What benefits does this mod bring to our community and
|
|
why should we consider it?
|
|
</FieldDescription>
|
|
<Textarea v-bind="field" placeholder="Share your thoughts..." rows="3"
|
|
class="resize-none mt-2" />
|
|
<div class="h-4">
|
|
<FieldError v-if="e.length" :errors="e" />
|
|
</div>
|
|
</Field>
|
|
</VeeField>
|
|
</div>
|
|
</div>
|
|
|
|
<Separator />
|
|
|
|
<!-- SECTION: Testing & Verification -->
|
|
<div class="space-y-5">
|
|
<div class="space-y-1">
|
|
<h3 class="font-semibold text-sm text-foreground">Testing & Verification</h3>
|
|
<p class="text-xs text-muted-foreground">Your experience with this mod</p>
|
|
</div>
|
|
|
|
<div class="space-y-5">
|
|
<!-- Confirmed Tested -->
|
|
<VeeField name="confirmed_tested" v-slot="{ field, errors: e }">
|
|
<Field :data-invalid="!!e.length">
|
|
<div class="flex items-center gap-3 p-3 rounded-md bg-muted/30 border border-border/50">
|
|
<Checkbox :model-value="field.value" @update:model-value="field.onChange"
|
|
class="hover:cursor-pointer" />
|
|
<div class="flex-1">
|
|
<FieldLabel class="font-medium text-md cursor-pointer">Testing & Stability
|
|
Confirmation
|
|
</FieldLabel>
|
|
<FieldDescription class="text-sm">I confirm that I have personally tested this
|
|
mod and to the best of my ability have verified that it functions as
|
|
described without
|
|
causing game-breaking bugs,
|
|
critical stability issues, or unintended performance degradation.
|
|
</FieldDescription>
|
|
</div>
|
|
</div>
|
|
<div class="h-4">
|
|
<FieldError v-if="e.length" :errors="e" />
|
|
</div>
|
|
</Field>
|
|
</VeeField>
|
|
</div>
|
|
</div>
|
|
|
|
<Separator />
|
|
|
|
<!-- SECTION: Compatibility -->
|
|
<div class="space-y-5">
|
|
<div class="space-y-1">
|
|
<h3 class="font-semibold text-sm text-foreground">Compatibility & Conflicts</h3>
|
|
<p class="text-xs text-muted-foreground">How does it work with our current setup?</p>
|
|
</div>
|
|
|
|
<div class="space-y-5">
|
|
<!-- Detrimental Effects -->
|
|
<VeeField name="detrimental_effects" v-slot="{ field, errors: e }">
|
|
<Field :data-invalid="!!e.length">
|
|
<FieldLabel class="text-sm font-medium">Potential Issues</FieldLabel>
|
|
<FieldDescription class="text-xs">Any negative impacts or concerns you noticed (Keybind
|
|
conflicts sould be noted in the dedicated section)?
|
|
</FieldDescription>
|
|
<Textarea v-bind="field" placeholder="List any issues... (leave blank if none)" rows="3"
|
|
class="resize-none mt-2" />
|
|
<div class="h-4">
|
|
<FieldError v-if="e.length" :errors="e" />
|
|
</div>
|
|
</Field>
|
|
</VeeField>
|
|
|
|
<!-- Keybind Conflicts -->
|
|
<VeeField name="keybind_conflicts" v-slot="{ field, errors: e }">
|
|
<Field :data-invalid="!!e.length">
|
|
<FieldLabel class="text-sm font-medium">Keybind Conflicts</FieldLabel>
|
|
<FieldDescription class="text-xs">Identify any controls that conflict with the existing
|
|
modpack along with resolutions for those conflicts.
|
|
</FieldDescription>
|
|
<Textarea v-bind="field"
|
|
placeholder='List specific conflicts and resolutions here, or type "None" if there are no conflicts.'
|
|
rows="3" class="resize-none mt-2" />
|
|
<div class="h-4">
|
|
<FieldError v-if="e.length" :errors="e" />
|
|
</div>
|
|
</Field>
|
|
</VeeField>
|
|
</div>
|
|
</div>
|
|
|
|
<Separator />
|
|
|
|
<!-- SECTION: Additional Notes -->
|
|
<div class="space-y-5">
|
|
<div class="space-y-5">
|
|
<!-- Special Considerations -->
|
|
<VeeField name="special_considerations" v-slot="{ field, errors: e }">
|
|
<Field :data-invalid="!!e.length">
|
|
<FieldLabel class="text-sm font-medium">Additional Information</FieldLabel>
|
|
<FieldDescription class="text-xs">Anything else we should know?</FieldDescription>
|
|
<Textarea v-bind="field" placeholder="Add any other important notes... (optional)"
|
|
rows="4" class="resize-none mt-2" />
|
|
<div class="h-4">
|
|
<FieldError v-if="e.length" :errors="e" />
|
|
</div>
|
|
</Field>
|
|
</VeeField>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Action Buttons -->
|
|
<div class="flex gap-2 justify-end pt-4">
|
|
<Button type="button" variant="outline" @click="resetForm">Clear</Button>
|
|
<Button type="submit" :disabled="submitting">
|
|
{{ submitting ? 'Submitting...' : 'Submit Request' }}
|
|
</Button>
|
|
</div>
|
|
</form>
|
|
</CardContent>
|
|
</Card>
|
|
</template> |