Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion frontend/src/components/Sidebar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@
<div class="flex flex-col gap-4">
<slot name="documentation">
<p
class="text-sm leading-normal tracking-tight font-medium text-primary-600"
v-if="hasDocs"
class="text-sm leading-normal tracking-tight font-medium text-primary-600"
>
Need help? Checkout the Ballot
<span class="flex items-center gap-1">
Expand Down
16 changes: 11 additions & 5 deletions frontend/src/components/dashboard/SubmissionCard.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
<template>
<router-link :to="submission.route">
<router-link
:to="{
name: 'Edit Candiature Submission',
params: { id: submission.name },
}"
>
<Card
class="!shadow-none border border-primary-200 hover:border-primary-500 hover:cursor-pointer transition-colors ease-in-out"
>
Expand All @@ -14,11 +19,12 @@
submission.status
]
"
></Tag>
<span class="text-sm"
>Submitted on
{{ dayjs(submission.creation).format('DD MMM YYYY') }}</span
>
</Tag>
<span class="text-sm">
Submitted on
{{ dayjs(submission.creation).format('DD MMM YYYY') }}
</span>
</div>
</template>
</Card>
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/form/RenderField.vue
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
</div>
<component
:is="getComponent"
id="field.fieldname"
:id="field.fieldname"
v-model="fields[getFieldIndex(field.fieldname)]['value']"
:options="field.options.split('\n')"
class="h-full"
Expand Down
1 change: 0 additions & 1 deletion frontend/src/components/form/RenderForm.vue
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
import { transformFields } from '@/utils/formbuilder'
import { computed, ref, inject } from 'vue'
import RenderBaseFields from '@/components/candidature/RenderBaseFields.vue'
import RenderField from './RenderField.vue'
import { toast } from 'vue-sonner'
import { createResource, ErrorMessage } from 'frappe-ui'
import { useRouter } from 'vue-router'
Expand Down
148 changes: 148 additions & 0 deletions frontend/src/components/submission/EditSubmissionForm.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
<template>
<div class="w-full flex flex-col">
<RenderBaseFields
v-model:fields="baseFieldsData"
:submission="submission"
/>
<RenderSection
v-for="(values, section) in transformedFields"
:key="section"
:fields="fieldsData"
:values="values"
:section="section"
:submission="submission"
/>
<ErrorMessage class="my-2" :message="errorMessages" />
<Button
class="w-full md:w-1/3"
label="Save"
variant="solid"
@click="handleUpdate"
/>
</div>
</template>

<script setup>
import { ref, computed, watch } from 'vue'
import RenderBaseFields from '@/components/candidature/RenderBaseFields.vue'
import RenderSection from '../form/RenderSection.vue'
import { transformFields } from '@/utils/formbuilder'
import { ErrorMessage } from 'frappe-ui'

const submission = defineModel({ required: true, type: Object })

const emit = defineEmits(['update-submission'])

const errorMessages = ref('')

// Initialize base fields with values from submission if they exist
const baseFieldsData = ref([
{
label: 'Photo',
fieldname: 'photo',
type: 'Attach Image',
mandatory: false,
value: submission.value?.photo || '',
},
{
label: 'Full Name',
fieldname: 'full_name',
type: 'data',
mandatory: true,
value: submission.value?.full_name || '',
},
{
label: 'Email',
fieldname: 'email',
type: 'data',
mandatory: true,
value: submission.value?.email || '',
},
{
label: 'Designation',
fieldname: 'designation',
type: 'data',
mandatory: true,
value: submission.value?.designation || '',
},
{
label: 'Organization',
fieldname: 'organization',
type: 'data',
mandatory: true,
value: submission.value?.organization || '',
},
])

// Initialize fields data from submission meta if it exists
const fieldsData = ref(
submission.value?.submission_meta
? JSON.parse(submission.value.submission_meta)
: [],
)

// Transform fields for section rendering
const transformedFields = computed(() => {
return transformFields(fieldsData.value)
})

// Watch for changes in baseFieldsData and update submission
watch(
baseFieldsData,
(newValues) => {
submission.value = {
...submission.value,
photo: newValues[0].value,
full_name: newValues[1].value,
email: newValues[2].value,
designation: newValues[3].value,
organization: newValues[4].value,
}
},
{ deep: true },
)

// Watch for changes in fieldsData and update submission
watch(
fieldsData,
(newValues) => {
submission.value = {
...submission.value,
submission_meta: JSON.stringify(newValues),
}
},
{ deep: true },
)

function getMandatoryErrors() {
let errors = []

// Check base fields using baseFieldsData
baseFieldsData.value.forEach((field) => {
if (field.mandatory && !field.value) {
errors.push(`${field.label} is mandatory`)
}
})

// Check section fields using fieldsData
fieldsData.value.forEach((field) => {
if (field.mandatory && !field.value) {
errors.push(`${field.label} is mandatory`)
}
})

return errors
}
const handleUpdate = () => {
let errors = getMandatoryErrors()

if (errors.length) {
errorMessages.value = errors.join('\n')
return
}

errorMessages.value = ''

emit('update-submission')
}
</script>
41 changes: 41 additions & 0 deletions frontend/src/components/submission/EditSubmissionHeader.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<template>
<div class="flex flex-col gap-3">
<h1 class="text-3xl font-sans font-bold tracking-wide">Edit Submission</h1>
<div
v-if="election"
class="flex gap-2 text-base text-primary-500 font-medium items-center"
>
<span>{{ election?.title }}</span>
<IconCircleFilled class="w-2 h-2" />
<Badge :label="`Form ${formStatus}`" :theme="getTheme" />
</div>
<hr />
</div>
</template>
<script setup>
import { IconCircleFilled } from '@tabler/icons-vue'
import { Badge } from 'frappe-ui'
import { computed } from 'vue'
const props = defineProps({
election: {
type: Object,
required: true,
},
formStatus: {
type: String,
default: '',
},
})

const getTheme = computed(() => {
if (!props.formStatus) {
return
}

if (props.formStatus == 'Live') {
return 'green'
}

return 'gray'
})
</script>
110 changes: 110 additions & 0 deletions frontend/src/pages/submissions/SubmissionEdit.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
<template>
<div class="flex flex-col md:flex-row">
<Sidebar>
<template #pre-nav-items>
<div>
<Button
label="Go Home"
icon-left="arrow-left"
variant="ghost"
:route="`/`"
/>
</div>
<div class="space-y-2">
<h4 class="text-sm uppercase mb-0 font-semibold">Edit Submission</h4>
<p class="text-sm text-primary-500">
Edit candidature submission. Changes can be made till the form is
live.
</p>
</div>
</template>
</Sidebar>
<div v-if="inLoading" class="w-full md:ml-[220px] px-4 py-6">
<LoadingText />
</div>
<div
v-if="!inLoading && formStatus.data"
class="w-full md:ml-[220px] px-4 py-6 flex flex-col gap-6"
>
<EditSubmissionHeader
:election="election.data"
:form-status="formStatus.data.status"
/>
<EditSubmissionForm
v-model="submission.doc"
@update-submission="updateSubmission"
/>
</div>
</div>
</template>
<script setup>
import Sidebar from '@/components/Sidebar.vue'
import { createDocumentResource, createResource, LoadingText } from 'frappe-ui'
import { useRoute } from 'vue-router'
import EditSubmissionHeader from '@/components/submission/EditSubmissionHeader.vue'
import EditSubmissionForm from '@/components/submission/EditSubmissionForm.vue'
import { ref, watch } from 'vue'
import { toast } from 'vue-sonner'

const route = useRoute()
const inLoading = ref(true)

const election = createResource({
url: 'frappe.client.get_value',
makeParams() {
return {
doctype: 'Election',
fieldname: ['title', 'slug', 'name'],
filters: {
name: submission.doc.election,
},
}
},
auto: false,
onSuccess(data) {
if (data) {
inLoading.value = false
}
},
onError(err) {
toast.error('Error fetching the election doc' + err.message)
},
})

const formStatus = createResource({
url: 'frappe.client.get_value',
makeParams() {
return {
doctype: 'Election Nomination Form',
filters: {
election: submission.election,
},
fieldname: 'status',
}
},
})

const submission = createDocumentResource({
doctype: 'Election Candidate Application',
name: route.params.id,
onError(err) {
toast.error('Error fetching the submission' + err.message)
},
})

watch(
() => submission.doc,
(newDoc) => {
if (newDoc != null) {
formStatus.fetch()
election.fetch()
}
},
)

const updateSubmission = () => {
submission.save.fetch().then(() => {
toast.success('Saved')
})
}
</script>
5 changes: 5 additions & 0 deletions frontend/src/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,11 @@ const routes = [
name: 'Nomination Form',
component: () => import('@/pages/election/CandidatureFormPublic.vue'),
},
{
path: '/submissions/:id',
name: 'Edit Candiature Submission',
component: () => import('@/pages/submissions/SubmissionEdit.vue'),
},
{
path: '/election/:slug/c/:id',
name: 'Candidature Submission Public',
Expand Down
Loading