mirror of
https://github.com/whekin/household-bot.git
synced 2026-03-31 19:34:02 +00:00
feat(miniapp): show topic routing in admin settings
This commit is contained in:
@@ -41,7 +41,14 @@ function onboardingRepository(): HouseholdConfigurationRepository {
|
|||||||
}) satisfies HouseholdTopicBindingRecord,
|
}) satisfies HouseholdTopicBindingRecord,
|
||||||
getHouseholdTopicBinding: async () => null,
|
getHouseholdTopicBinding: async () => null,
|
||||||
findHouseholdTopicByTelegramContext: async () => null,
|
findHouseholdTopicByTelegramContext: async () => null,
|
||||||
listHouseholdTopicBindings: async () => [],
|
listHouseholdTopicBindings: async () => [
|
||||||
|
{
|
||||||
|
householdId: household.householdId,
|
||||||
|
role: 'purchase',
|
||||||
|
telegramThreadId: '2',
|
||||||
|
topicName: 'Общие покупки'
|
||||||
|
}
|
||||||
|
],
|
||||||
listReminderTargets: async () => [],
|
listReminderTargets: async () => [],
|
||||||
upsertHouseholdJoinToken: async (input) => ({
|
upsertHouseholdJoinToken: async (input) => ({
|
||||||
householdId: household.householdId,
|
householdId: household.householdId,
|
||||||
@@ -381,6 +388,14 @@ describe('createMiniAppSettingsHandler', () => {
|
|||||||
utilitiesReminderDay: 3,
|
utilitiesReminderDay: 3,
|
||||||
timezone: 'Asia/Tbilisi'
|
timezone: 'Asia/Tbilisi'
|
||||||
},
|
},
|
||||||
|
topics: [
|
||||||
|
{
|
||||||
|
householdId: 'household-1',
|
||||||
|
role: 'purchase',
|
||||||
|
telegramThreadId: '2',
|
||||||
|
topicName: 'Общие покупки'
|
||||||
|
}
|
||||||
|
],
|
||||||
categories: [],
|
categories: [],
|
||||||
members: [
|
members: [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -384,6 +384,7 @@ export function createMiniAppSettingsHandler(options: {
|
|||||||
ok: true,
|
ok: true,
|
||||||
authorized: true,
|
authorized: true,
|
||||||
settings: serializeBillingSettings(result.settings),
|
settings: serializeBillingSettings(result.settings),
|
||||||
|
topics: result.topics,
|
||||||
categories: result.categories,
|
categories: result.categories,
|
||||||
members: result.members
|
members: result.members
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -272,6 +272,19 @@ function App() {
|
|||||||
: copy().paymentLedgerRent
|
: copy().paymentLedgerRent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function topicRoleLabel(role: 'purchase' | 'feedback' | 'reminders' | 'payments'): string {
|
||||||
|
switch (role) {
|
||||||
|
case 'purchase':
|
||||||
|
return copy().topicPurchase
|
||||||
|
case 'feedback':
|
||||||
|
return copy().topicFeedback
|
||||||
|
case 'reminders':
|
||||||
|
return copy().topicReminders
|
||||||
|
case 'payments':
|
||||||
|
return copy().topicPayments
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function loadDashboard(initData: string) {
|
async function loadDashboard(initData: string) {
|
||||||
try {
|
try {
|
||||||
setDashboard(await fetchMiniAppDashboard(initData))
|
setDashboard(await fetchMiniAppDashboard(initData))
|
||||||
@@ -1393,6 +1406,33 @@ function App() {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
|
|
||||||
|
<article class="balance-item">
|
||||||
|
<header>
|
||||||
|
<strong>{copy().topicBindingsTitle}</strong>
|
||||||
|
<span>{String(adminSettings()?.topics.length ?? 0)}/4</span>
|
||||||
|
</header>
|
||||||
|
<p>{copy().topicBindingsBody}</p>
|
||||||
|
<div class="balance-list admin-sublist">
|
||||||
|
{(['purchase', 'feedback', 'reminders', 'payments'] as const).map((role) => {
|
||||||
|
const binding = adminSettings()?.topics.find((topic) => topic.role === role)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<article class="ledger-item">
|
||||||
|
<header>
|
||||||
|
<strong>{topicRoleLabel(role)}</strong>
|
||||||
|
<span>{binding ? copy().topicBound : copy().topicUnbound}</span>
|
||||||
|
</header>
|
||||||
|
<p>
|
||||||
|
{binding
|
||||||
|
? `${binding.topicName ?? `Topic #${binding.telegramThreadId}`} · #${binding.telegramThreadId}`
|
||||||
|
: copy().topicUnbound}
|
||||||
|
</p>
|
||||||
|
</article>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|||||||
@@ -71,6 +71,15 @@ export const dictionary = {
|
|||||||
latestActivityEmpty: 'Recent utility and purchase entries will appear here.',
|
latestActivityEmpty: 'Recent utility and purchase entries will appear here.',
|
||||||
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',
|
||||||
|
topicBindingsBody:
|
||||||
|
'Review which Telegram topics are currently connected for purchases, feedback, reminders, and payments.',
|
||||||
|
topicPurchase: 'Purchases',
|
||||||
|
topicFeedback: 'Feedback',
|
||||||
|
topicReminders: 'Reminders',
|
||||||
|
topicPayments: 'Payments',
|
||||||
|
topicBound: 'Bound',
|
||||||
|
topicUnbound: 'Unbound',
|
||||||
billingSettingsTitle: 'Billing settings',
|
billingSettingsTitle: 'Billing settings',
|
||||||
settlementCurrency: 'Settlement currency',
|
settlementCurrency: 'Settlement currency',
|
||||||
billingCycleTitle: 'Current billing cycle',
|
billingCycleTitle: 'Current billing cycle',
|
||||||
@@ -194,6 +203,15 @@ export const dictionary = {
|
|||||||
latestActivityEmpty: 'Здесь появятся последние коммунальные платежи и покупки.',
|
latestActivityEmpty: 'Здесь появятся последние коммунальные платежи и покупки.',
|
||||||
householdSettingsTitle: 'Настройки household',
|
householdSettingsTitle: 'Настройки household',
|
||||||
householdSettingsBody: 'Здесь можно менять язык household и подтверждать новых соседей.',
|
householdSettingsBody: 'Здесь можно менять язык household и подтверждать новых соседей.',
|
||||||
|
topicBindingsTitle: 'Привязанные топики',
|
||||||
|
topicBindingsBody:
|
||||||
|
'Проверь, какие Telegram-топики сейчас подключены для покупок, обратной связи, напоминаний и оплат.',
|
||||||
|
topicPurchase: 'Покупки',
|
||||||
|
topicFeedback: 'Обратная связь',
|
||||||
|
topicReminders: 'Напоминания',
|
||||||
|
topicPayments: 'Оплаты',
|
||||||
|
topicBound: 'Привязан',
|
||||||
|
topicUnbound: 'Не привязан',
|
||||||
billingSettingsTitle: 'Настройки биллинга',
|
billingSettingsTitle: 'Настройки биллинга',
|
||||||
settlementCurrency: 'Валюта расчёта',
|
settlementCurrency: 'Валюта расчёта',
|
||||||
billingCycleTitle: 'Текущий billing cycle',
|
billingCycleTitle: 'Текущий billing cycle',
|
||||||
|
|||||||
@@ -64,6 +64,12 @@ export interface MiniAppUtilityCategory {
|
|||||||
isActive: boolean
|
isActive: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface MiniAppTopicBinding {
|
||||||
|
role: 'purchase' | 'feedback' | 'reminders' | 'payments'
|
||||||
|
telegramThreadId: string
|
||||||
|
topicName: string | null
|
||||||
|
}
|
||||||
|
|
||||||
export interface MiniAppDashboard {
|
export interface MiniAppDashboard {
|
||||||
period: string
|
period: string
|
||||||
currency: 'USD' | 'GEL'
|
currency: 'USD' | 'GEL'
|
||||||
@@ -104,6 +110,7 @@ export interface MiniAppDashboard {
|
|||||||
|
|
||||||
export interface MiniAppAdminSettingsPayload {
|
export interface MiniAppAdminSettingsPayload {
|
||||||
settings: MiniAppBillingSettings
|
settings: MiniAppBillingSettings
|
||||||
|
topics: readonly MiniAppTopicBinding[]
|
||||||
categories: readonly MiniAppUtilityCategory[]
|
categories: readonly MiniAppUtilityCategory[]
|
||||||
members: readonly MiniAppMember[]
|
members: readonly MiniAppMember[]
|
||||||
}
|
}
|
||||||
@@ -349,6 +356,7 @@ export async function fetchMiniAppAdminSettings(
|
|||||||
ok: boolean
|
ok: boolean
|
||||||
authorized?: boolean
|
authorized?: boolean
|
||||||
settings?: MiniAppBillingSettings
|
settings?: MiniAppBillingSettings
|
||||||
|
topics?: MiniAppTopicBinding[]
|
||||||
categories?: MiniAppUtilityCategory[]
|
categories?: MiniAppUtilityCategory[]
|
||||||
members?: MiniAppMember[]
|
members?: MiniAppMember[]
|
||||||
error?: string
|
error?: string
|
||||||
@@ -358,6 +366,7 @@ export async function fetchMiniAppAdminSettings(
|
|||||||
!response.ok ||
|
!response.ok ||
|
||||||
!payload.authorized ||
|
!payload.authorized ||
|
||||||
!payload.settings ||
|
!payload.settings ||
|
||||||
|
!payload.topics ||
|
||||||
!payload.categories ||
|
!payload.categories ||
|
||||||
!payload.members
|
!payload.members
|
||||||
) {
|
) {
|
||||||
@@ -366,6 +375,7 @@ export async function fetchMiniAppAdminSettings(
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
settings: payload.settings,
|
settings: payload.settings,
|
||||||
|
topics: payload.topics,
|
||||||
categories: payload.categories,
|
categories: payload.categories,
|
||||||
members: payload.members
|
members: payload.members
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,14 @@ function repository(): HouseholdConfigurationRepository {
|
|||||||
}),
|
}),
|
||||||
getHouseholdTopicBinding: async () => null,
|
getHouseholdTopicBinding: async () => null,
|
||||||
findHouseholdTopicByTelegramContext: async () => null,
|
findHouseholdTopicByTelegramContext: async () => null,
|
||||||
listHouseholdTopicBindings: async () => [],
|
listHouseholdTopicBindings: async () => [
|
||||||
|
{
|
||||||
|
householdId: 'household-1',
|
||||||
|
role: 'purchase',
|
||||||
|
telegramThreadId: '2',
|
||||||
|
topicName: 'Общие покупки'
|
||||||
|
}
|
||||||
|
],
|
||||||
listReminderTargets: async () => [],
|
listReminderTargets: async () => [],
|
||||||
upsertHouseholdJoinToken: async () => ({
|
upsertHouseholdJoinToken: async () => ({
|
||||||
householdId: 'household-1',
|
householdId: 'household-1',
|
||||||
@@ -167,7 +174,7 @@ function repository(): HouseholdConfigurationRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
describe('createMiniAppAdminService', () => {
|
describe('createMiniAppAdminService', () => {
|
||||||
test('returns billing settings, utility categories, and members for admins', async () => {
|
test('returns billing settings, topic bindings, utility categories, and members for admins', async () => {
|
||||||
const service = createMiniAppAdminService(repository())
|
const service = createMiniAppAdminService(repository())
|
||||||
|
|
||||||
const result = await service.getSettings({
|
const result = await service.getSettings({
|
||||||
@@ -188,6 +195,14 @@ describe('createMiniAppAdminService', () => {
|
|||||||
utilitiesReminderDay: 3,
|
utilitiesReminderDay: 3,
|
||||||
timezone: 'Asia/Tbilisi'
|
timezone: 'Asia/Tbilisi'
|
||||||
},
|
},
|
||||||
|
topics: [
|
||||||
|
{
|
||||||
|
householdId: 'household-1',
|
||||||
|
role: 'purchase',
|
||||||
|
telegramThreadId: '2',
|
||||||
|
topicName: 'Общие покупки'
|
||||||
|
}
|
||||||
|
],
|
||||||
categories: [],
|
categories: [],
|
||||||
members: []
|
members: []
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import type {
|
|||||||
HouseholdConfigurationRepository,
|
HouseholdConfigurationRepository,
|
||||||
HouseholdMemberRecord,
|
HouseholdMemberRecord,
|
||||||
HouseholdPendingMemberRecord,
|
HouseholdPendingMemberRecord,
|
||||||
|
HouseholdTopicBindingRecord,
|
||||||
HouseholdUtilityCategoryRecord
|
HouseholdUtilityCategoryRecord
|
||||||
} from '@household/ports'
|
} from '@household/ports'
|
||||||
import { Money, type CurrencyCode } from '@household/domain'
|
import { Money, type CurrencyCode } from '@household/domain'
|
||||||
@@ -27,6 +28,7 @@ export interface MiniAppAdminService {
|
|||||||
settings: HouseholdBillingSettingsRecord
|
settings: HouseholdBillingSettingsRecord
|
||||||
categories: readonly HouseholdUtilityCategoryRecord[]
|
categories: readonly HouseholdUtilityCategoryRecord[]
|
||||||
members: readonly HouseholdMemberRecord[]
|
members: readonly HouseholdMemberRecord[]
|
||||||
|
topics: readonly HouseholdTopicBindingRecord[]
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
status: 'rejected'
|
status: 'rejected'
|
||||||
@@ -138,17 +140,19 @@ export function createMiniAppAdminService(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const [settings, categories, members] = await Promise.all([
|
const [settings, categories, members, topics] = await Promise.all([
|
||||||
repository.getHouseholdBillingSettings(input.householdId),
|
repository.getHouseholdBillingSettings(input.householdId),
|
||||||
repository.listHouseholdUtilityCategories(input.householdId),
|
repository.listHouseholdUtilityCategories(input.householdId),
|
||||||
repository.listHouseholdMembers(input.householdId)
|
repository.listHouseholdMembers(input.householdId),
|
||||||
|
repository.listHouseholdTopicBindings(input.householdId)
|
||||||
])
|
])
|
||||||
|
|
||||||
return {
|
return {
|
||||||
status: 'ok',
|
status: 'ok',
|
||||||
settings,
|
settings,
|
||||||
categories,
|
categories,
|
||||||
members
|
members,
|
||||||
|
topics
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user