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
22 changes: 7 additions & 15 deletions apps/web-app/app/components/PartnerAgreementCard.vue
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
<template>
<UCard
v-if="agreement"
variant="subtle"
class="h-full"
>
<UCard class="h-full" @click="modalUpdatePartnerAgreement.open({ agreementId: agreement.id })">
<div class="flex flex-col gap-3">
<div class="flex flex-row items-start gap-3.5">
<UIcon name="i-lucide-scroll-text" class="shrink-0 size-16 text-secondary" />
Expand Down Expand Up @@ -45,24 +41,18 @@
</p>
</div>
</UCard>
<CreateCard
v-else
label="Добавить договор"
icon="i-lucide-scroll-text"
@click="modalCreatePartnerAgreement.open({ partnerId, legalEntityId })"
/>
</template>

<script setup lang="ts">
import type { PartnerAgreement } from '@roll-stack/database'
import { ModalCreatePartnerAgreement } from '#components'
import { ModalUpdatePartnerAgreement } from '#components'
import { format } from 'date-fns'
import { ru } from 'date-fns/locale/ru'

const { agreement } = defineProps<{ partnerId: string, legalEntityId: string, agreement: PartnerAgreement | null | undefined }>()
const { agreement } = defineProps<{ agreement: PartnerAgreement }>()

const overlay = useOverlay()
const modalCreatePartnerAgreement = overlay.create(ModalCreatePartnerAgreement)
const modalUpdatePartnerAgreement = overlay.create(ModalUpdatePartnerAgreement)

const agreementProgress = computed(() => {
if (!agreement?.willEndAt || !agreement?.concludedAt) {
Expand All @@ -73,6 +63,8 @@ const agreementProgress = computed(() => {
const concludedAt = new Date(agreement.concludedAt)
const willEndAt = new Date(agreement.willEndAt)

return Math.floor(100 - ((now.getTime() - concludedAt.getTime()) / (willEndAt.getTime() - concludedAt.getTime())) * 100)
const res = Math.floor(100 - ((now.getTime() - concludedAt.getTime()) / (willEndAt.getTime() - concludedAt.getTime())) * 100)

return res > 0 ? res : 0
})
</script>
4 changes: 3 additions & 1 deletion apps/web-app/app/components/PartnerCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ const agreementProgress = computed(() => {
const concludedAt = new Date(partner.activeAgreement.concludedAt)
const willEndAt = new Date(partner.activeAgreement.willEndAt)

return Math.floor(100 - ((now.getTime() - concludedAt.getTime()) / (willEndAt.getTime() - concludedAt.getTime())) * 100)
const res = Math.floor(100 - ((now.getTime() - concludedAt.getTime()) / (willEndAt.getTime() - concludedAt.getTime())) * 100)

return res > 0 ? res : 0
})
</script>
32 changes: 27 additions & 5 deletions apps/web-app/app/components/form/CreatePartnerAgreement.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<template>
<UForm
ref="form"
:validate="createValidator(createPartnerAgreementSchema)"
:state="state"
class="flex flex-col gap-3"
Expand Down Expand Up @@ -29,7 +30,6 @@
<UFormField
label="Дата окончания"
name="willEndAt"
required
>
<UInput
:value="selectedWillEndAt ? df.format(selectedWillEndAt.toDate(getLocalTimeZone())) : ''"
Expand All @@ -45,15 +45,23 @@
</template>
</UPopover>

<UFormField label="Номер договора (внутренний)" name="internalId">
<UFormField
label="Номер договора (внутренний)"
name="internalId"
required
>
<UInput
v-model="state.internalId"
size="xl"
class="w-full items-center justify-center"
/>
</UFormField>

<UFormField label="Роялти, %" name="royalty">
<UFormField
label="Роялти, %"
name="royalty"
required
>
<UInputNumber
v-model="state.royalty"
orientation="vertical"
Expand All @@ -63,7 +71,11 @@
/>
</UFormField>

<UFormField label="Мин. роялти, руб" name="minRoyaltyPerMonth">
<UFormField
label="Мин. роялти, руб"
name="minRoyaltyPerMonth"
required
>
<UInputNumber
v-model="state.minRoyaltyPerMonth"
orientation="vertical"
Expand All @@ -72,7 +84,11 @@
/>
</UFormField>

<UFormField label="Паушальный взнос, руб" name="lumpSumPayment">
<UFormField
label="Паушальный взнос, руб"
name="lumpSumPayment"
required
>
<UInputNumber
v-model="state.lumpSumPayment"
orientation="vertical"
Expand Down Expand Up @@ -117,6 +133,8 @@ const actionToast = useActionToast()

const partnerStore = usePartnerStore()

const form = useTemplateRef('form')

const state = ref<Partial<CreatePartnerAgreement>>({
concludedAt: undefined,
willEndAt: undefined,
Expand All @@ -139,11 +157,15 @@ watch(selectedConcludedAt, () => {
state.value.concludedAt = new Date(
`${selectedConcludedAt.value?.toString()} 12:00:00`,
).toISOString()

form.value?.clear()
})
watch(selectedWillEndAt, () => {
state.value.willEndAt = new Date(
`${selectedWillEndAt.value?.toString()} 12:00:00`,
).toISOString()

form.value?.clear()
})

async function onSubmit(event: FormSubmitEvent<CreatePartnerAgreement>) {
Expand Down
121 changes: 121 additions & 0 deletions apps/web-app/app/components/form/UpdatePartnerAgreement.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
<template>
<UForm
:validate="createValidator(updatePartnerAgreementSchema)"
:state="state"
class="flex flex-col gap-3"
@submit="onSubmit"
>
<UFormField label="Роялти, %" name="royalty">
<UInputNumber
v-model="state.royalty"
orientation="vertical"
:step="0.1"
size="xl"
class="w-full items-center justify-center"
/>
</UFormField>

<UFormField label="Мин. роялти, руб" name="minRoyaltyPerMonth">
<UInputNumber
v-model="state.minRoyaltyPerMonth"
orientation="vertical"
size="xl"
class="w-full items-center justify-center"
/>
</UFormField>

<UFormField label="Маркетинговый сбор, %" name="marketingFee">
<UInputNumber
v-model="state.marketingFee"
orientation="vertical"
size="xl"
class="w-full items-center justify-center"
/>
</UFormField>

<UFormField label="Мин. маркетинговый сбор, руб" name="minMarketingFeePerMonth">
<UInputNumber
v-model="state.minMarketingFeePerMonth"
orientation="vertical"
size="xl"
class="w-full items-center justify-center"
/>
</UFormField>

<UFormField label="Паушальный взнос, руб" name="lumpSumPayment">
<UInputNumber
v-model="state.lumpSumPayment"
orientation="vertical"
size="xl"
class="w-full items-center justify-center"
/>
</UFormField>

<UFormField :label="$t('common.comment')" name="comment">
<UInput
v-model="state.comment"
size="xl"
placeholder="Для внутреннего использования"
class="w-full items-center justify-center"
/>
</UFormField>

<UButton
type="submit"
variant="solid"
color="secondary"
size="xl"
block
class="mt-3"
:label="$t('common.update')"
/>
</UForm>
</template>

<script setup lang="ts">
import type { UpdatePartnerAgreement } from '#shared/services/partner'
import type { FormSubmitEvent } from '@nuxt/ui'
import { updatePartnerAgreementSchema } from '#shared/services/partner'

const { agreementId } = defineProps<{
agreementId: string
}>()

const emit = defineEmits(['success', 'submitted'])

const { t } = useI18n()
const actionToast = useActionToast()

const partnerStore = usePartnerStore()
const partner = computed(() => partnerStore.partners.find((partner) => partner.activeAgreementId === agreementId))
const agreement = computed(() => partner.value?.activeAgreement)

const state = ref<Partial<UpdatePartnerAgreement>>({
royalty: agreement.value?.royalty,
minRoyaltyPerMonth: agreement.value?.minRoyaltyPerMonth,
marketingFee: agreement.value?.marketingFee,
minMarketingFeePerMonth: agreement.value?.minMarketingFeePerMonth,
lumpSumPayment: agreement.value?.lumpSumPayment,
comment: agreement.value?.comment ?? undefined,
})

async function onSubmit(event: FormSubmitEvent<UpdatePartnerAgreement>) {
const toastId = actionToast.start()
emit('submitted')

try {
await $fetch(`/api/partner/agreement/id/${agreementId}`, {
method: 'PATCH',
body: event.data,
})

await partnerStore.update()

actionToast.success(toastId, t('toast.partner-agreement-updated'))
emit('success')
} catch (error) {
console.error(error)
actionToast.error(toastId)
}
}
</script>
19 changes: 19 additions & 0 deletions apps/web-app/app/components/modal/UpdatePartnerAgreement.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<template>
<UModal title="Обновление договора">
<template #body>
<FormUpdatePartnerAgreement
:agreement-id="agreementId"
@submitted="overlay.closeAll"
@success="overlay.closeAll"
/>
</template>
</UModal>
</template>

<script setup lang="ts">
defineProps<{
agreementId: string
}>()

const overlay = useOverlay()
</script>
26 changes: 24 additions & 2 deletions apps/web-app/app/pages/partner/[id].vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,28 +7,39 @@
class="flex-1 -ml-2.5"
/>

<UButton
<!-- <UButton
size="lg"
variant="solid"
color="secondary"
class="w-full md:w-fit"
icon="i-lucide-square-pen"
:label="t('common.edit')"
@click="() => {}"
/>
/> -->
</template>
</Header>

<NuxtPage />
</template>

<script setup lang="ts">
import { format } from 'date-fns'
import { ru } from 'date-fns/locale/ru'

const { t } = useI18n()
const { params } = useRoute('partner-id')

const partnerStore = usePartnerStore()
const partner = computed(() => partnerStore.partners.find((partner) => partner.id === params.id))

const activeAgreementTo = computed(() => {
if (!partner.value?.activeAgreement?.willEndAt) {
return 'отсутствует'
}

return `до ${format(new Date(partner.value?.activeAgreement?.willEndAt), 'd MMMM yyyy', { locale: ru })}`
})

const submenuItems = computed(() => [
{
label: t('common.partner'),
Expand All @@ -42,6 +53,17 @@ const submenuItems = computed(() => [
icon: 'i-lucide-map-pinned',
badge: partner.value?.kitchens.length,
},
{
label: 'Договор',
to: `/partner/${partner.value?.id}/agreement`,
icon: 'i-lucide-scroll-text',
badge: activeAgreementTo.value,
},
{
label: 'Юр. лицо',
to: `/partner/${partner.value?.id}/legal`,
icon: 'i-lucide-scale',
},
])

useHead({
Expand Down
34 changes: 34 additions & 0 deletions apps/web-app/app/pages/partner/[id]/agreement.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<template>
<Content>
<div class="grid grid-cols-1 gap-4 md:gap-6 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
<div v-if="partner?.activeAgreement" class="lg:col-span-2">
<PartnerAgreementCard :agreement="partner.activeAgreement" />
</div>

<div>
<CreateCard
label="Добавить новый договор"
icon="i-lucide-scroll-text"
@click="modalCreatePartnerAgreement.open({ partnerId: partner?.id, legalEntityId: partner?.legalEntity?.id })"
/>
</div>
</div>
</Content>
</template>

<script setup lang="ts">
import { ModalCreatePartnerAgreement } from '#components'

const { t } = useI18n()
const { params } = useRoute('partner-id')

const partnerStore = usePartnerStore()
const partner = computed(() => partnerStore.partners.find((partner) => partner.id === params.id))

const overlay = useOverlay()
const modalCreatePartnerAgreement = overlay.create(ModalCreatePartnerAgreement)

useHead({
title: t('app.partner-agreement.title'),
})
</script>
Loading