201 lines
6.5 KiB
Svelte
201 lines
6.5 KiB
Svelte
<script lang="ts">
|
|
import { organization, loadOrganization } from "$lib/stores/organization";
|
|
import { user } from "$lib/stores/auth";
|
|
import { apiFetch } from "$lib/api/client";
|
|
import { onMount } from "svelte";
|
|
import { resolveAssetUrl } from "$lib/utils/url";
|
|
import { t } from "svelte-i18n";
|
|
|
|
let name = "";
|
|
let logoUrl = "";
|
|
let language = "en";
|
|
let logoFile: File | null = null;
|
|
let loading = false;
|
|
let success = false;
|
|
let error = "";
|
|
|
|
const MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB
|
|
|
|
let logoUrlInput: HTMLInputElement;
|
|
let logoFileInput: HTMLInputElement;
|
|
|
|
onMount(async () => {
|
|
if ($user?.organizationId) {
|
|
await loadOrganization($user.organizationId);
|
|
if ($organization) {
|
|
name = $organization.name;
|
|
logoUrl = $organization.logoUrl || "";
|
|
language = $organization.language || "en";
|
|
}
|
|
}
|
|
});
|
|
|
|
async function handleSubmit() {
|
|
const orgId = $user?.organizationId;
|
|
if (!orgId) {
|
|
error = "User organization ID is missing";
|
|
return;
|
|
}
|
|
|
|
loading = true;
|
|
success = false;
|
|
error = "";
|
|
try {
|
|
let finalLogoUrl = logoUrl;
|
|
if (logoFile) {
|
|
const formData = new FormData();
|
|
formData.append('file', logoFile);
|
|
const assetResponse = await apiFetch<any>(`/assets/organization/${orgId}/logo`, {
|
|
method: 'POST',
|
|
body: formData,
|
|
});
|
|
// Update local logoUrl with the actual URL from the backend
|
|
finalLogoUrl = assetResponse.logoUrl;
|
|
logoUrl = finalLogoUrl;
|
|
}
|
|
|
|
const updated = await apiFetch<any>(`/organizations/${orgId}`, {
|
|
method: 'PATCH',
|
|
body: JSON.stringify({ name, logoUrl: finalLogoUrl || "", language }),
|
|
});
|
|
organization.set(updated);
|
|
success = true;
|
|
logoFile = null;
|
|
} catch (e: any) {
|
|
error = e.message || "Failed to update organization";
|
|
} finally {
|
|
loading = false;
|
|
}
|
|
}
|
|
|
|
function handleFileChange(e: Event) {
|
|
const target = e.target as HTMLInputElement;
|
|
const file = target.files?.[0];
|
|
if (file) {
|
|
if (file.size > MAX_FILE_SIZE) {
|
|
error = "File size exceeds 10MB limit";
|
|
target.value = "";
|
|
return;
|
|
}
|
|
logoFile = file;
|
|
// If a file is selected, clear the logoUrl to avoid ambiguity
|
|
logoUrl = "";
|
|
error = "";
|
|
}
|
|
}
|
|
|
|
function handleUrlChange() {
|
|
// If the user starts typing a URL, clear the file selection
|
|
if (logoFile) {
|
|
logoFile = null;
|
|
if (logoFileInput) {
|
|
logoFileInput.value = "";
|
|
}
|
|
}
|
|
}
|
|
|
|
// Reactive preview URL
|
|
$: previewUrl = logoFile ? URL.createObjectURL(logoFile) : resolveAssetUrl(logoUrl);
|
|
|
|
function handleRemoveLogo() {
|
|
logoUrl = "";
|
|
logoFile = null;
|
|
}
|
|
</script>
|
|
|
|
<div class="max-w-2xl mx-auto py-8">
|
|
<h2 class="text-2xl font-bold text-gray-900 mb-6">{$t('organization.settings')}</h2>
|
|
|
|
<div class="bg-white shadow rounded-lg p-6">
|
|
<form on:submit|preventDefault={handleSubmit} class="space-y-6">
|
|
<div>
|
|
<label for="orgName" class="block text-sm font-medium text-gray-700">{$t('organization.name')}</label>
|
|
<input
|
|
id="orgName"
|
|
type="text"
|
|
bind:value={name}
|
|
required
|
|
class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm"
|
|
placeholder="Enter organization name"
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<label for="orgLanguage" class="block text-sm font-medium text-gray-700">{$t('common.language')}</label>
|
|
<select
|
|
id="orgLanguage"
|
|
bind:value={language}
|
|
class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm"
|
|
>
|
|
<option value="en">English</option>
|
|
<option value="es-ES">Español (Ibérico)</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div>
|
|
<label for="logoFile" class="block text-sm font-medium text-gray-700">{$t('organization.logo')}</label>
|
|
<div class="mt-1 flex items-center space-x-4">
|
|
<input
|
|
id="logoFile"
|
|
type="file"
|
|
accept="image/*"
|
|
bind:this={logoFileInput}
|
|
on:change={handleFileChange}
|
|
class="block w-full text-sm text-gray-500 file:mr-4 file:py-2 file:px-4 file:rounded-md file:border-0 file:text-sm file:font-semibold file:bg-indigo-50 file:text-indigo-700 hover:file:bg-indigo-100"
|
|
/>
|
|
</div>
|
|
<p class="mt-2 text-sm text-gray-500">Upload a logo for your organization (max 10MB).</p>
|
|
</div>
|
|
|
|
<div>
|
|
<label for="logoUrl" class="block text-sm font-medium text-gray-700">Logo URL (Optional)</label>
|
|
<input
|
|
id="logoUrl"
|
|
type="text"
|
|
bind:value={logoUrl}
|
|
on:input={handleUrlChange}
|
|
class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm"
|
|
placeholder="https://example.com/logo.png"
|
|
/>
|
|
<p class="mt-2 text-sm text-gray-500">Or provide a URL for your organization's logo.</p>
|
|
</div>
|
|
|
|
{#if previewUrl}
|
|
<div class="mt-4">
|
|
<div class="flex items-center justify-between mb-2">
|
|
<p class="text-sm font-medium text-gray-700">Logo Preview:</p>
|
|
<button
|
|
type="button"
|
|
on:click={handleRemoveLogo}
|
|
class="text-sm text-red-600 hover:text-red-800"
|
|
>
|
|
Remove Logo
|
|
</button>
|
|
</div>
|
|
<div class="w-20 h-20 border rounded-lg flex items-center justify-center overflow-hidden bg-gray-50">
|
|
<img src={previewUrl} alt="Logo preview" class="max-w-full max-h-full object-contain" />
|
|
</div>
|
|
</div>
|
|
{/if}
|
|
|
|
{#if error}
|
|
<div class="text-red-600 text-sm mt-2">{error}</div>
|
|
{/if}
|
|
|
|
{#if success}
|
|
<div class="text-green-600 text-sm mt-2">{$t('organization.updateSuccess')}</div>
|
|
{/if}
|
|
|
|
<div class="flex justify-end">
|
|
<button
|
|
type="submit"
|
|
disabled={loading}
|
|
class="bg-indigo-600 border border-transparent rounded-md shadow-sm py-2 px-4 inline-flex justify-center text-sm font-medium text-white hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
|
|
>
|
|
{loading ? $t('common.loading') : $t('common.save')}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|