Files
optima/ui/src/routes/companies/[id]/components/CompanySidebar.svelte
T

317 lines
10 KiB
Svelte

<script lang="ts">
import { goto } from "$app/navigation";
import EmailText from "../../../../components/EmailText.svelte";
import {
type CompanyData,
companyInitials,
statusClass,
formatDate,
formatPhone,
formatAddress,
} from "../types";
import type { PermissionMap } from "$lib/permissions";
export let company: CompanyData | null;
export let permissions: PermissionMap;
export let isMobile: boolean;
export let mobileActiveTab: string | null;
// Selected site for the sites dropdown
let selectedSiteUid: string | null = null;
$: allAddresses = company?.cw_Data?.allAddresses ?? [];
$: activeSites = allAddresses.filter((a) => !a.inactiveFlag);
$: selectedSite =
selectedSiteUid !== null
? activeSites.find((a) => a.uid === selectedSiteUid) ?? null
: activeSites.find((a) => a.defaultFlag) ?? activeSites[0] ?? null;
function formatSiteAddress(site: (typeof activeSites)[number]): string[] {
const lines: string[] = [];
if (site.addressLine1) lines.push(site.addressLine1);
if (site.addressLine2) lines.push(site.addressLine2);
const cityStateZip = [site.city, site.state, site.zip]
.filter(Boolean)
.join(", ");
if (cityStateZip) lines.push(cityStateZip);
if (site.country) lines.push(site.country);
return lines;
}
</script>
<div
class="company-detail-left"
class:mobile-collapsed={isMobile && mobileActiveTab !== null}
>
<div class="detail-pane-body">
<button
class="back-btn"
on:click={() => goto("/companies")}
aria-label="Back to companies"
>
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
width="16"
height="16"
>
<path d="M19 12H5M12 19l-7-7 7-7" />
</svg>
</button>
{#if company}
<!-- Avatar + name + status -->
<div class="profile-header">
<div class="profile-avatar">
<span class="profile-initials">{companyInitials(company.name)}</span>
</div>
<h3 class="profile-name">{company.name}</h3>
{#if company.status}
<span class="profile-status {statusClass(company.status)}"
>{company.status}</span
>
{/if}
</div>
<!-- Info rows -->
<div class="profile-info">
{#if company.cw_Data?.primaryContact}
{@const contact = company.cw_Data.primaryContact}
<div class="primary-contact-section">
<div class="primary-contact-header">
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
width="14"
height="14"
>
<path d="M20 21v-2a4 4 0 00-4-4H8a4 4 0 00-4 4v2" />
<circle cx="12" cy="7" r="4" />
</svg>
<span class="primary-contact-label">Primary Contact</span>
</div>
<div class="primary-contact-card">
<div class="primary-contact-name">
{[contact.firstName, contact.lastName]
.filter(Boolean)
.join(" ")}
{#if contact.inactive}
<span class="primary-contact-inactive">Inactive</span>
{/if}
</div>
{#if contact.title}
<div class="primary-contact-title">{contact.title}</div>
{/if}
{#if contact.email}
<div class="primary-contact-detail">
<EmailText email={contact.email} />
</div>
{/if}
{#if contact.phone}
<div class="primary-contact-detail">
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
width="12"
height="12"
>
<path
d="M22 16.92v3a2 2 0 01-2.18 2 19.79 19.79 0 01-8.63-3.07 19.5 19.5 0 01-6-6A19.79 19.79 0 012.12 4.18 2 2 0 014.11 2h3a2 2 0 012 1.72c.127.96.361 1.903.7 2.81a2 2 0 01-.45 2.11L8.09 9.91a16 16 0 006 6l1.27-1.27a2 2 0 012.11-.45c.907.339 1.85.573 2.81.7A2 2 0 0122 16.92z"
/>
</svg>
<span>{formatPhone(contact.phone)}</span>
</div>
{/if}
</div>
</div>
{/if}
{#if company.type}
<div class="info-row">
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
class="info-icon"
>
<rect x="2" y="7" width="20" height="14" rx="2" ry="2" />
<path d="M16 3h-8l-2 4h12z" />
</svg>
<div class="info-content">
<span class="info-label">Type</span>
<span class="info-value">{company.type}</span>
</div>
</div>
{/if}
{#if company.contactEmail}
<div class="info-row">
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
class="info-icon"
>
<rect x="2" y="4" width="20" height="16" rx="2" />
<path d="M22 7l-10 7L2 7" />
</svg>
<div class="info-content">
<span class="info-label">Email</span>
<span class="info-value">
<EmailText email={company.contactEmail} />
</span>
</div>
</div>
{/if}
{#if company.contactPhone}
<div class="info-row">
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
class="info-icon"
>
<path
d="M22 16.92v3a2 2 0 01-2.18 2 19.79 19.79 0 01-8.63-3.07 19.5 19.5 0 01-6-6A19.79 19.79 0 012.12 4.18 2 2 0 014.11 2h3a2 2 0 012 1.72c.127.96.361 1.903.7 2.81a2 2 0 01-.45 2.11L8.09 9.91a16 16 0 006 6l1.27-1.27a2 2 0 012.11-.45c.907.339 1.85.573 2.81.7A2 2 0 0122 16.92z"
/>
</svg>
<div class="info-content">
<span class="info-label">Phone</span>
<span class="info-value">{formatPhone(company.contactPhone)}</span
>
</div>
</div>
{/if}
{#if permissions["company.fetch.address"] && activeSites.length > 0}
<div class="info-row">
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
class="info-icon"
>
<path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0118 0z" />
<circle cx="12" cy="10" r="3" />
</svg>
<div class="info-content">
{#if activeSites.length > 1}
<select
class="site-select"
bind:value={selectedSiteUid}
on:change={(e) => (selectedSiteUid = e.currentTarget.value)}
>
{#each activeSites as site}
<option value={site.uid}>
{site.name}{site.defaultFlag ? " (Default)" : ""}
</option>
{/each}
</select>
{:else}
<span class="info-label"
>{activeSites[0]?.name ?? "Address"}</span
>
{/if}
{#if selectedSite}
{@const lines = formatSiteAddress(selectedSite)}
{#if lines.length > 0}
<span class="info-value address-multiline">
{#each lines as line}
{line}<br />
{/each}
</span>
{/if}
{#if selectedSite.phone}
<span class="info-value site-phone"
>{formatPhone(selectedSite.phone)}</span
>
{/if}
{/if}
</div>
</div>
{:else if permissions["company.fetch.address"] && formatAddress(company).length > 0}
<div class="info-row">
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
class="info-icon"
>
<path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0118 0z" />
<circle cx="12" cy="10" r="3" />
</svg>
<div class="info-content">
<span class="info-label">Address</span>
<span class="info-value address-multiline">
{#each formatAddress(company) as line}
{line}<br />
{/each}
</span>
</div>
</div>
{/if}
{#if formatDate(company.createdAt)}
<div class="info-row">
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
class="info-icon"
>
<rect x="3" y="4" width="18" height="18" rx="2" ry="2" />
<line x1="16" y1="2" x2="16" y2="6" />
<line x1="8" y1="2" x2="8" y2="6" />
<line x1="3" y1="10" x2="21" y2="10" />
</svg>
<div class="info-content">
<span class="info-label">Created</span>
<span class="info-value">{formatDate(company.createdAt)}</span>
</div>
</div>
{/if}
{#if formatDate(company.updatedAt)}
<div class="info-row">
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
class="info-icon"
>
<circle cx="12" cy="12" r="10" />
<polyline points="12 6 12 12 16 14" />
</svg>
<div class="info-content">
<span class="info-label">Updated</span>
<span class="info-value">{formatDate(company.updatedAt)}</span>
</div>
</div>
{/if}
</div>
{#if company.identifier || company.id}
<div class="side-pane-identifier">
{company.identifier || company.id}
</div>
{/if}
{:else}
<div class="profile-empty">
<p>Company not found.</p>
</div>
{/if}
</div>
</div>