317 lines
10 KiB
Svelte
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>
|