WIP jordan
This commit is contained in:
178
app/pages/settings/organization.vue
Normal file
178
app/pages/settings/organization.vue
Normal file
@@ -0,0 +1,178 @@
|
||||
<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>
|
||||
Reference in New Issue
Block a user