179 lines
6.5 KiB
Vue
179 lines
6.5 KiB
Vue
<script setup lang="ts">
|
|
import { MAX_LOGO_FILE_BYTES } from '~/types/branding'
|
|
import type { BrokerageBrandingState } from '~/types/branding'
|
|
|
|
definePageMeta({ ssr: false })
|
|
|
|
usePageTitle('Organization · Settings')
|
|
|
|
const { isSuperAdmin } = useSuperAdmin()
|
|
const toast = useToast()
|
|
const { saved, defaultBrokerageBranding } = useBrokerageBranding()
|
|
const draft = ref<BrokerageBrandingState>(defaultBrokerageBranding())
|
|
const fileInputRef = ref<HTMLInputElement | null>(null)
|
|
|
|
function syncDraftFromSaved() {
|
|
const s = saved.value
|
|
draft.value = {
|
|
companyName: s.companyName,
|
|
logoDataUrl: s.logoDataUrl,
|
|
logoFileName: s.logoFileName,
|
|
reportPageHeader: s.reportPageHeader,
|
|
reportPageFooter: s.reportPageFooter
|
|
}
|
|
}
|
|
|
|
onMounted(() => syncDraftFromSaved())
|
|
watch(saved, () => syncDraftFromSaved(), { deep: true })
|
|
|
|
function pickFile() {
|
|
fileInputRef.value?.click()
|
|
}
|
|
|
|
function onLogoFile(e: Event) {
|
|
const input = e.target as HTMLInputElement
|
|
const file = input.files?.[0]
|
|
input.value = ''
|
|
if (!file) return
|
|
if (!file.type.startsWith('image/')) {
|
|
toast.add({ title: 'Choose an image file', color: 'error' })
|
|
return
|
|
}
|
|
if (file.size > MAX_LOGO_FILE_BYTES) {
|
|
toast.add({
|
|
title: 'Image too large',
|
|
description: `Use a file under ${Math.round(MAX_LOGO_FILE_BYTES / 1024)} KB.`,
|
|
color: 'error'
|
|
})
|
|
return
|
|
}
|
|
const reader = new FileReader()
|
|
reader.onload = () => {
|
|
draft.value.logoDataUrl = reader.result as string
|
|
draft.value.logoFileName = file.name
|
|
}
|
|
reader.onerror = () => {
|
|
toast.add({ title: 'Could not read file', color: 'error' })
|
|
}
|
|
reader.readAsDataURL(file)
|
|
}
|
|
|
|
function clearLogo() {
|
|
draft.value.logoDataUrl = null
|
|
draft.value.logoFileName = ''
|
|
}
|
|
|
|
function save() {
|
|
saved.value = { ...draft.value }
|
|
toast.add({
|
|
title: 'Organization saved',
|
|
description: 'Sidebar and home use this brokerage identity for all users.',
|
|
color: 'success'
|
|
})
|
|
}
|
|
|
|
function cancel() {
|
|
syncDraftFromSaved()
|
|
toast.add({ title: 'Changes discarded', color: 'neutral' })
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div class="mx-auto max-w-3xl space-y-8">
|
|
<div class="flex flex-wrap items-center gap-x-3 gap-y-2 text-sm">
|
|
<AppBackToHome />
|
|
<span class="text-[var(--text-muted)] opacity-50" aria-hidden="true">|</span>
|
|
<NuxtLink to="/settings" class="font-medium text-[var(--text-muted)] transition hover:text-[var(--text-primary)]">
|
|
All settings
|
|
</NuxtLink>
|
|
</div>
|
|
|
|
<UAlert
|
|
v-if="!isSuperAdmin"
|
|
color="warning"
|
|
variant="soft"
|
|
title="Tenant administrators only"
|
|
description="Company name, logo, and report headers are managed here for the whole organization. Ask a superadmin to update them or to grant you access."
|
|
/>
|
|
|
|
<template v-else>
|
|
<div>
|
|
<h1 class="mt-1 text-2xl font-semibold tracking-tight text-[var(--text-primary)]">Organization</h1>
|
|
<p class="mt-2 max-w-2xl text-[14px] leading-relaxed text-[var(--text-muted)]">
|
|
Brokerage legal name and logo in the app chrome; report header and footer for PDFs and exports. This applies to
|
|
the whole tenant — not personal account settings.
|
|
</p>
|
|
</div>
|
|
|
|
<UCard class="overflow-hidden border-[var(--sidebar-border)] bg-[var(--surface)] ring-1 ring-black/5">
|
|
<div class="border-b border-[var(--sidebar-border)] bg-[var(--page-bg)] px-5 py-3 text-sm text-[var(--text-primary)]">
|
|
General / General
|
|
</div>
|
|
<div class="space-y-6 p-5">
|
|
<UFormField label="Company legal name" class="w-full">
|
|
<UInput
|
|
v-model="draft.companyName"
|
|
placeholder="Brokerage legal name as registered"
|
|
class="w-full"
|
|
/>
|
|
</UFormField>
|
|
<div class="flex flex-col gap-4 sm:flex-row sm:items-start">
|
|
<div class="min-w-0 flex-1 space-y-2">
|
|
<p class="text-sm font-medium text-[var(--text-primary)]">Company logo (sidebar & home)</p>
|
|
<input ref="fileInputRef" type="file" accept="image/*" class="sr-only" @change="onLogoFile" />
|
|
<div class="flex flex-wrap items-center gap-2">
|
|
<UButton color="primary" size="sm" @click="pickFile">Browse…</UButton>
|
|
<UButton v-if="draft.logoDataUrl" color="neutral" variant="ghost" size="sm" @click="clearLogo">
|
|
Remove logo
|
|
</UButton>
|
|
</div>
|
|
<p v-if="draft.logoFileName" class="text-xs text-[var(--text-muted)]">Current file: {{ draft.logoFileName }}</p>
|
|
<p class="text-xs text-[var(--text-muted)]">Transparent PNGs work best with the sidebar chrome treatment.</p>
|
|
</div>
|
|
<div
|
|
class="app-logo-chrome flex h-28 min-w-[200px] shrink-0 items-center justify-center rounded-lg border border-dashed border-[var(--sidebar-border)] px-4 py-3"
|
|
>
|
|
<img
|
|
v-if="draft.logoDataUrl"
|
|
:src="draft.logoDataUrl"
|
|
:alt="draft.companyName || 'Brokerage logo'"
|
|
class="max-h-24 max-w-full object-contain"
|
|
/>
|
|
<span v-else class="text-center text-xs text-[var(--text-muted)]">Logo preview</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</UCard>
|
|
|
|
<UCard class="overflow-hidden border-[var(--sidebar-border)] bg-[var(--surface)] ring-1 ring-black/5">
|
|
<div class="border-b border-[var(--sidebar-border)] bg-[var(--page-bg)] px-5 py-3 text-sm text-[var(--text-primary)]">
|
|
Reports / Reportes
|
|
</div>
|
|
<div class="space-y-4 p-5">
|
|
<UFormField label="Page header (PDFs)" class="w-full">
|
|
<UTextarea
|
|
v-model="draft.reportPageHeader"
|
|
:rows="4"
|
|
placeholder="Address, phone, RUC — top of generated reports."
|
|
class="w-full font-mono text-sm"
|
|
/>
|
|
</UFormField>
|
|
<UFormField label="Page footer (PDFs)" class="w-full">
|
|
<UTextarea
|
|
v-model="draft.reportPageFooter"
|
|
:rows="4"
|
|
placeholder="Legal disclaimer, validity, contact — footer for PDFs."
|
|
class="w-full font-mono text-sm"
|
|
/>
|
|
</UFormField>
|
|
</div>
|
|
</UCard>
|
|
|
|
<div class="flex flex-wrap gap-3">
|
|
<UButton color="neutral" variant="soft" @click="cancel">Cancel</UButton>
|
|
<UButton color="primary" icon="i-heroicons-check" @click="save">Save</UButton>
|
|
</div>
|
|
</template>
|
|
</div>
|
|
</template>
|