Skip to content

Commit 702727a

Browse files
authored
feat: legal and agreement create actions (#15)
* feat: legal and agreement create actions * chore: some updates
1 parent 9766024 commit 702727a

13 files changed

Lines changed: 607 additions & 103 deletions

File tree

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
<template>
2+
<UCard
3+
v-if="agreement"
4+
variant="subtle"
5+
class="h-full"
6+
>
7+
<div class="flex flex-col gap-3">
8+
<div class="flex flex-row items-start gap-3.5">
9+
<UIcon name="i-lucide-scroll-text" class="shrink-0 size-16 text-secondary" />
10+
11+
<UProgress
12+
v-model="agreementProgress"
13+
size="lg"
14+
color="secondary"
15+
status
16+
/>
17+
</div>
18+
19+
<h3 class="text-xl md:text-xl/6 font-semibold">
20+
Договор №{{ agreement.internalId }}
21+
</h3>
22+
23+
<div>
24+
<p v-if="agreement.concludedAt">
25+
Заключен: {{ format(new Date(agreement.concludedAt), 'd MMMM yyyy', { locale: ru }) }}
26+
</p>
27+
<p v-if="agreement.willEndAt">
28+
Действует до: {{ format(new Date(agreement.willEndAt), 'd MMMM yyyy', { locale: ru }) }}
29+
</p>
30+
<p>Роялти: {{ agreement.royalty }}%</p>
31+
<p>Мин. роялти: {{ agreement.minRoyaltyPerMonth }} ₽ / месяц</p>
32+
33+
<p v-if="agreement.marketingFee">
34+
Маркетинговый сбор: {{ agreement.marketingFee }}%
35+
</p>
36+
<p v-if="agreement.minMarketingFeePerMonth">
37+
Мин. маркетинговый сбор: {{ agreement.minMarketingFeePerMonth }} ₽ / месяц
38+
</p>
39+
40+
<p>Паушальный взнос: {{ agreement.lumpSumPayment }} ₽</p>
41+
</div>
42+
43+
<p class="text-muted">
44+
{{ agreement.comment }}
45+
</p>
46+
</div>
47+
</UCard>
48+
<CreateCard
49+
v-else
50+
label="Добавить договор"
51+
icon="i-lucide-scroll-text"
52+
@click="modalCreatePartnerAgreement.open({ partnerId, legalEntityId })"
53+
/>
54+
</template>
55+
56+
<script setup lang="ts">
57+
import type { PartnerAgreement } from '@roll-stack/database'
58+
import { ModalCreatePartnerAgreement } from '#components'
59+
import { format } from 'date-fns'
60+
import { ru } from 'date-fns/locale/ru'
61+
62+
const { agreement } = defineProps<{ partnerId: string, legalEntityId: string, agreement: PartnerAgreement | null | undefined }>()
63+
64+
const overlay = useOverlay()
65+
const modalCreatePartnerAgreement = overlay.create(ModalCreatePartnerAgreement)
66+
67+
const agreementProgress = computed(() => {
68+
if (!agreement?.willEndAt || !agreement?.concludedAt) {
69+
return 0
70+
}
71+
72+
const now = new Date()
73+
const concludedAt = new Date(agreement.concludedAt)
74+
const willEndAt = new Date(agreement.willEndAt)
75+
76+
return Math.floor(100 - ((now.getTime() - concludedAt.getTime()) / (willEndAt.getTime() - concludedAt.getTime())) * 100)
77+
})
78+
</script>

apps/web-app/app/components/PartnerCard.vue

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,12 @@
88
:class="{ 'opacity-75 grayscale group-hover:grayscale-0 group-hover:opacity-100': imagesMode === 'grayscale' }"
99
>
1010

11-
<div class="absolute top-2 left-0 right-0 w-full">
11+
<div
12+
class="absolute top-2 left-0 right-0 w-full opacity-0 group-hover:opacity-100 duration-200"
13+
:class="[
14+
agreementProgress <= 15 && 'opacity-100',
15+
]"
16+
>
1217
<div class="mx-2 px-2 py-1 bg-default/97 rounded-lg flex flex-row items-center gap-1.5">
1318
<UIcon name="i-lucide-scroll-text" class="shrink-0 size-5 text-secondary" />
1419

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<template>
2+
<UCard v-if="entity" class="h-full">
3+
<div class="flex flex-col gap-2.5">
4+
<UIcon name="i-lucide-scale" class="size-16 text-muted/25" />
5+
6+
<h3 class="text-xl md:text-xl/6 font-semibold">
7+
{{ entity.name }}
8+
</h3>
9+
10+
<div>
11+
<p>ИНН {{ entity.inn }}</p>
12+
<p>ОГРНИП {{ entity.ogrnip }}</p>
13+
</div>
14+
15+
<p class="text-muted">
16+
{{ entity.comment }}
17+
</p>
18+
</div>
19+
</UCard>
20+
<CreateCard
21+
v-else
22+
label="Добавить юридическое лицо"
23+
icon="i-lucide-scale"
24+
@click="modalCreatePartnerLegalEntity.open({ partnerId })"
25+
/>
26+
</template>
27+
28+
<script setup lang="ts">
29+
import type { PartnerLegalEntity } from '@roll-stack/database'
30+
import { ModalCreatePartnerLegalEntity } from '#components'
31+
32+
defineProps<{ partnerId: string, entity: PartnerLegalEntity | null | undefined }>()
33+
34+
const overlay = useOverlay()
35+
const modalCreatePartnerLegalEntity = overlay.create(ModalCreatePartnerLegalEntity)
36+
</script>
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
<template>
2+
<UForm
3+
:validate="createValidator(createPartnerAgreementSchema)"
4+
:state="state"
5+
class="flex flex-col gap-3"
6+
@submit="onSubmit"
7+
>
8+
<UPopover>
9+
<UFormField
10+
label="Дата заключения"
11+
name="concludedAt"
12+
required
13+
>
14+
<UInput
15+
:value="selectedConcludedAt ? df.format(selectedConcludedAt.toDate(getLocalTimeZone())) : ''"
16+
placeholder="Выберите дату"
17+
size="xl"
18+
class="w-full items-center justify-center cursor-pointer"
19+
:ui="{ trailing: 'pe-1.5' }"
20+
/>
21+
</UFormField>
22+
23+
<template #content>
24+
<UCalendar v-model="selectedConcludedAt" class="p-2" />
25+
</template>
26+
</UPopover>
27+
28+
<UPopover>
29+
<UFormField
30+
label="Дата окончания"
31+
name="willEndAt"
32+
required
33+
>
34+
<UInput
35+
:value="selectedWillEndAt ? df.format(selectedWillEndAt.toDate(getLocalTimeZone())) : ''"
36+
placeholder="Выберите дату"
37+
size="xl"
38+
class="w-full items-center justify-center cursor-pointer"
39+
:ui="{ trailing: 'pe-1.5' }"
40+
/>
41+
</UFormField>
42+
43+
<template #content>
44+
<UCalendar v-model="selectedWillEndAt" class="p-2" />
45+
</template>
46+
</UPopover>
47+
48+
<UFormField label="Номер договора (внутренний)" name="internalId">
49+
<UInput
50+
v-model="state.internalId"
51+
size="xl"
52+
class="w-full items-center justify-center"
53+
/>
54+
</UFormField>
55+
56+
<UFormField label="Роялти, %" name="royalty">
57+
<UInputNumber
58+
v-model="state.royalty"
59+
orientation="vertical"
60+
:step="0.1"
61+
size="xl"
62+
class="w-full items-center justify-center"
63+
/>
64+
</UFormField>
65+
66+
<UFormField label="Мин. роялти, руб" name="minRoyaltyPerMonth">
67+
<UInputNumber
68+
v-model="state.minRoyaltyPerMonth"
69+
orientation="vertical"
70+
size="xl"
71+
class="w-full items-center justify-center"
72+
/>
73+
</UFormField>
74+
75+
<UFormField label="Паушальный взнос, руб" name="lumpSumPayment">
76+
<UInputNumber
77+
v-model="state.lumpSumPayment"
78+
orientation="vertical"
79+
size="xl"
80+
class="w-full items-center justify-center"
81+
/>
82+
</UFormField>
83+
84+
<UFormField :label="$t('common.comment')" name="comment">
85+
<UInput
86+
v-model="state.comment"
87+
size="xl"
88+
placeholder="Для внутреннего использования"
89+
class="w-full items-center justify-center"
90+
/>
91+
</UFormField>
92+
93+
<UButton
94+
type="submit"
95+
variant="solid"
96+
color="secondary"
97+
size="xl"
98+
block
99+
class="mt-3"
100+
:label="$t('common.create')"
101+
/>
102+
</UForm>
103+
</template>
104+
105+
<script setup lang="ts">
106+
import type { CreatePartnerAgreement } from '#shared/services/partner'
107+
import type { CalendarDate } from '@internationalized/date'
108+
import type { FormSubmitEvent } from '@nuxt/ui'
109+
import { createPartnerAgreementSchema } from '#shared/services/partner'
110+
import { DateFormatter, getLocalTimeZone } from '@internationalized/date'
111+
112+
const { partnerId, legalEntityId } = defineProps<{ partnerId: string, legalEntityId: string }>()
113+
const emit = defineEmits(['success', 'submitted'])
114+
115+
const { t } = useI18n()
116+
const actionToast = useActionToast()
117+
118+
const partnerStore = usePartnerStore()
119+
120+
const state = ref<Partial<CreatePartnerAgreement>>({
121+
concludedAt: undefined,
122+
willEndAt: undefined,
123+
internalId: undefined,
124+
royalty: undefined,
125+
minRoyaltyPerMonth: undefined,
126+
lumpSumPayment: undefined,
127+
comment: undefined,
128+
legalEntityId,
129+
})
130+
131+
const df = new DateFormatter('ru-RU', {
132+
dateStyle: 'long',
133+
})
134+
135+
const selectedConcludedAt = shallowRef<CalendarDate | undefined>()
136+
const selectedWillEndAt = shallowRef<CalendarDate | undefined>()
137+
138+
watch(selectedConcludedAt, () => {
139+
state.value.concludedAt = new Date(
140+
`${selectedConcludedAt.value?.toString()} 12:00:00`,
141+
).toISOString()
142+
})
143+
watch(selectedWillEndAt, () => {
144+
state.value.willEndAt = new Date(
145+
`${selectedWillEndAt.value?.toString()} 12:00:00`,
146+
).toISOString()
147+
})
148+
149+
async function onSubmit(event: FormSubmitEvent<CreatePartnerAgreement>) {
150+
const toastId = actionToast.start()
151+
emit('submitted')
152+
153+
try {
154+
await $fetch(`/api/partner/id/${partnerId}/agreement`, {
155+
method: 'POST',
156+
body: event.data,
157+
})
158+
159+
await partnerStore.update()
160+
161+
actionToast.success(toastId, t('toast.partner-agreement-created'))
162+
emit('success')
163+
} catch (error) {
164+
console.error(error)
165+
actionToast.error(toastId)
166+
}
167+
}
168+
</script>

0 commit comments

Comments
 (0)