mirror of
https://github.com/whekin/household-bot.git
synced 2026-03-31 14:04:04 +00:00
feat(miniapp): add billing review tools and house sections
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -35,6 +35,10 @@ export const dictionary = {
|
|||||||
balances: 'Balances',
|
balances: 'Balances',
|
||||||
ledger: 'Ledger',
|
ledger: 'Ledger',
|
||||||
house: 'House',
|
house: 'House',
|
||||||
|
houseSectionBilling: 'Billing',
|
||||||
|
houseSectionUtilities: 'Utilities',
|
||||||
|
houseSectionMembers: 'Members',
|
||||||
|
houseSectionTopics: 'Topics',
|
||||||
welcome: 'Welcome back',
|
welcome: 'Welcome back',
|
||||||
adminTag: 'Admin',
|
adminTag: 'Admin',
|
||||||
residentTag: 'Resident',
|
residentTag: 'Resident',
|
||||||
@@ -69,6 +73,22 @@ export const dictionary = {
|
|||||||
emptyDashboard: 'No billing cycle is ready yet.',
|
emptyDashboard: 'No billing cycle is ready yet.',
|
||||||
latestActivityTitle: 'Latest activity',
|
latestActivityTitle: 'Latest activity',
|
||||||
latestActivityEmpty: 'Recent utility and purchase entries will appear here.',
|
latestActivityEmpty: 'Recent utility and purchase entries will appear here.',
|
||||||
|
purchaseReviewTitle: 'Purchases',
|
||||||
|
purchaseReviewBody: 'Edit or remove purchases if the bot recorded the wrong item.',
|
||||||
|
paymentsAdminTitle: 'Payments',
|
||||||
|
paymentsAdminBody: 'Add, fix, or remove payment records for the current cycle.',
|
||||||
|
paymentsAddAction: 'Add payment',
|
||||||
|
addingPayment: 'Adding payment…',
|
||||||
|
paymentKind: 'Payment kind',
|
||||||
|
paymentAmount: 'Payment amount',
|
||||||
|
paymentMember: 'Member',
|
||||||
|
paymentSaveAction: 'Save payment',
|
||||||
|
paymentDeleteAction: 'Delete payment',
|
||||||
|
deletingPayment: 'Deleting payment…',
|
||||||
|
purchaseSaveAction: 'Save purchase',
|
||||||
|
purchaseDeleteAction: 'Delete purchase',
|
||||||
|
deletingPurchase: 'Deleting purchase…',
|
||||||
|
savingPurchase: 'Saving purchase…',
|
||||||
householdSettingsTitle: 'Household settings',
|
householdSettingsTitle: 'Household settings',
|
||||||
householdSettingsBody: 'Control household defaults and approve roommates who requested access.',
|
householdSettingsBody: 'Control household defaults and approve roommates who requested access.',
|
||||||
topicBindingsTitle: 'Topic bindings',
|
topicBindingsTitle: 'Topic bindings',
|
||||||
@@ -171,6 +191,10 @@ export const dictionary = {
|
|||||||
balances: 'Баланс',
|
balances: 'Баланс',
|
||||||
ledger: 'Леджер',
|
ledger: 'Леджер',
|
||||||
house: 'Дом',
|
house: 'Дом',
|
||||||
|
houseSectionBilling: 'Биллинг',
|
||||||
|
houseSectionUtilities: 'Коммуналка',
|
||||||
|
houseSectionMembers: 'Участники',
|
||||||
|
houseSectionTopics: 'Топики',
|
||||||
welcome: 'С возвращением',
|
welcome: 'С возвращением',
|
||||||
adminTag: 'Админ',
|
adminTag: 'Админ',
|
||||||
residentTag: 'Житель',
|
residentTag: 'Житель',
|
||||||
@@ -204,6 +228,23 @@ export const dictionary = {
|
|||||||
emptyDashboard: 'Пока нет готового billing cycle.',
|
emptyDashboard: 'Пока нет готового billing cycle.',
|
||||||
latestActivityTitle: 'Последняя активность',
|
latestActivityTitle: 'Последняя активность',
|
||||||
latestActivityEmpty: 'Здесь появятся последние коммунальные платежи и покупки.',
|
latestActivityEmpty: 'Здесь появятся последние коммунальные платежи и покупки.',
|
||||||
|
purchaseReviewTitle: 'Покупки',
|
||||||
|
purchaseReviewBody:
|
||||||
|
'Здесь можно исправить или удалить покупку, если бот распознал её неправильно.',
|
||||||
|
paymentsAdminTitle: 'Оплаты',
|
||||||
|
paymentsAdminBody: 'Добавляй, исправляй или удаляй оплаты за текущий цикл.',
|
||||||
|
paymentsAddAction: 'Добавить оплату',
|
||||||
|
addingPayment: 'Добавляем оплату…',
|
||||||
|
paymentKind: 'Тип оплаты',
|
||||||
|
paymentAmount: 'Сумма оплаты',
|
||||||
|
paymentMember: 'Участник',
|
||||||
|
paymentSaveAction: 'Сохранить оплату',
|
||||||
|
paymentDeleteAction: 'Удалить оплату',
|
||||||
|
deletingPayment: 'Удаляем оплату…',
|
||||||
|
purchaseSaveAction: 'Сохранить покупку',
|
||||||
|
purchaseDeleteAction: 'Удалить покупку',
|
||||||
|
deletingPurchase: 'Удаляем покупку…',
|
||||||
|
savingPurchase: 'Сохраняем покупку…',
|
||||||
householdSettingsTitle: 'Настройки household',
|
householdSettingsTitle: 'Настройки household',
|
||||||
householdSettingsBody: 'Здесь можно менять язык household и подтверждать новых соседей.',
|
householdSettingsBody: 'Здесь можно менять язык household и подтверждать новых соседей.',
|
||||||
topicBindingsTitle: 'Привязанные топики',
|
topicBindingsTitle: 'Привязанные топики',
|
||||||
|
|||||||
@@ -327,6 +327,26 @@ button {
|
|||||||
margin-top: 12px;
|
margin-top: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.section-switch {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-switch button {
|
||||||
|
border: 1px solid rgb(255 255 255 / 0.12);
|
||||||
|
border-radius: 16px;
|
||||||
|
min-height: 44px;
|
||||||
|
padding: 10px 12px;
|
||||||
|
background: rgb(255 255 255 / 0.04);
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-switch button.is-active {
|
||||||
|
border-color: rgb(247 179 137 / 0.7);
|
||||||
|
background: rgb(247 179 137 / 0.14);
|
||||||
|
}
|
||||||
|
|
||||||
.admin-layout {
|
.admin-layout {
|
||||||
gap: 18px;
|
gap: 18px;
|
||||||
}
|
}
|
||||||
@@ -396,7 +416,8 @@ button {
|
|||||||
padding: 12px 14px;
|
padding: 12px 14px;
|
||||||
background: rgb(255 255 255 / 0.04);
|
background: rgb(255 255 255 / 0.04);
|
||||||
color: inherit;
|
color: inherit;
|
||||||
line-height: 1.35;
|
font-size: 1rem;
|
||||||
|
line-height: 1.2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.settings-field__value {
|
.settings-field__value {
|
||||||
@@ -452,6 +473,13 @@ button {
|
|||||||
grid-column: 1 / -1;
|
grid-column: 1 / -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.activity-row p,
|
||||||
|
.ledger-item p,
|
||||||
|
.utility-bill-row p,
|
||||||
|
.balance-item p {
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
}
|
||||||
|
|
||||||
@media (min-width: 760px) {
|
@media (min-width: 760px) {
|
||||||
.shell {
|
.shell {
|
||||||
max-width: 920px;
|
max-width: 920px;
|
||||||
@@ -498,6 +526,10 @@ button {
|
|||||||
.balance-item--wide {
|
.balance-item--wide {
|
||||||
grid-column: 1 / -1;
|
grid-column: 1 / -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.section-switch {
|
||||||
|
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 980px) {
|
@media (min-width: 980px) {
|
||||||
|
|||||||
@@ -96,6 +96,7 @@ export interface MiniAppDashboard {
|
|||||||
id: string
|
id: string
|
||||||
kind: 'purchase' | 'utility' | 'payment'
|
kind: 'purchase' | 'utility' | 'payment'
|
||||||
title: string
|
title: string
|
||||||
|
memberId: string | null
|
||||||
paymentKind: 'rent' | 'utilities' | null
|
paymentKind: 'rent' | 'utilities' | null
|
||||||
amountMajor: string
|
amountMajor: string
|
||||||
currency: 'USD' | 'GEL'
|
currency: 'USD' | 'GEL'
|
||||||
@@ -731,3 +732,118 @@ export async function deleteMiniAppUtilityBill(
|
|||||||
|
|
||||||
return payload.cycleState
|
return payload.cycleState
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function updateMiniAppPurchase(
|
||||||
|
initData: string,
|
||||||
|
input: {
|
||||||
|
purchaseId: string
|
||||||
|
description: string
|
||||||
|
amountMajor: string
|
||||||
|
currency: 'USD' | 'GEL'
|
||||||
|
}
|
||||||
|
): Promise<void> {
|
||||||
|
const response = await fetch(`${apiBaseUrl()}/api/miniapp/admin/purchases/update`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'content-type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
initData,
|
||||||
|
...input
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
const payload = (await response.json()) as { ok: boolean; authorized?: boolean; error?: string }
|
||||||
|
if (!response.ok || !payload.authorized) {
|
||||||
|
throw new Error(payload.error ?? 'Failed to update purchase')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deleteMiniAppPurchase(initData: string, purchaseId: string): Promise<void> {
|
||||||
|
const response = await fetch(`${apiBaseUrl()}/api/miniapp/admin/purchases/delete`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'content-type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
initData,
|
||||||
|
purchaseId
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
const payload = (await response.json()) as { ok: boolean; authorized?: boolean; error?: string }
|
||||||
|
if (!response.ok || !payload.authorized) {
|
||||||
|
throw new Error(payload.error ?? 'Failed to delete purchase')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function addMiniAppPayment(
|
||||||
|
initData: string,
|
||||||
|
input: {
|
||||||
|
memberId: string
|
||||||
|
kind: 'rent' | 'utilities'
|
||||||
|
amountMajor: string
|
||||||
|
currency: 'USD' | 'GEL'
|
||||||
|
}
|
||||||
|
): Promise<void> {
|
||||||
|
const response = await fetch(`${apiBaseUrl()}/api/miniapp/admin/payments/add`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'content-type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
initData,
|
||||||
|
...input
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
const payload = (await response.json()) as { ok: boolean; authorized?: boolean; error?: string }
|
||||||
|
if (!response.ok || !payload.authorized) {
|
||||||
|
throw new Error(payload.error ?? 'Failed to add payment')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updateMiniAppPayment(
|
||||||
|
initData: string,
|
||||||
|
input: {
|
||||||
|
paymentId: string
|
||||||
|
memberId: string
|
||||||
|
kind: 'rent' | 'utilities'
|
||||||
|
amountMajor: string
|
||||||
|
currency: 'USD' | 'GEL'
|
||||||
|
}
|
||||||
|
): Promise<void> {
|
||||||
|
const response = await fetch(`${apiBaseUrl()}/api/miniapp/admin/payments/update`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'content-type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
initData,
|
||||||
|
...input
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
const payload = (await response.json()) as { ok: boolean; authorized?: boolean; error?: string }
|
||||||
|
if (!response.ok || !payload.authorized) {
|
||||||
|
throw new Error(payload.error ?? 'Failed to update payment')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deleteMiniAppPayment(initData: string, paymentId: string): Promise<void> {
|
||||||
|
const response = await fetch(`${apiBaseUrl()}/api/miniapp/admin/payments/delete`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'content-type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
initData,
|
||||||
|
paymentId
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
const payload = (await response.json()) as { ok: boolean; authorized?: boolean; error?: string }
|
||||||
|
if (!response.ok || !payload.authorized) {
|
||||||
|
throw new Error(payload.error ?? 'Failed to delete payment')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user