feat(sales): add quotes tab, PDF viewer, and opportunity sidebar enhancements
This commit is contained in:
@@ -11,6 +11,7 @@
|
||||
import ContactsTab from "./components/ContactsTab.svelte";
|
||||
import ActivityTab from "./components/ActivityTab.svelte";
|
||||
import ProductsTab from "./components/ProductsTab.svelte";
|
||||
import QuotesTab from "./components/QuotesTab.svelte";
|
||||
|
||||
export let data: PageData;
|
||||
|
||||
@@ -19,6 +20,7 @@
|
||||
$: notes = data.notes;
|
||||
$: contacts = data.contacts;
|
||||
$: products = data.products;
|
||||
$: quotes = data.quotes ?? [];
|
||||
$: permissions = data.permissions;
|
||||
let localProductSequence: number[] | null =
|
||||
data.opportunity?.productSequence ?? null;
|
||||
@@ -48,6 +50,7 @@
|
||||
const tabs = [
|
||||
"Overview",
|
||||
"Products",
|
||||
"Quotes",
|
||||
"Notes",
|
||||
"Contacts",
|
||||
"Activity",
|
||||
@@ -55,6 +58,11 @@
|
||||
type Tab = (typeof tabs)[number];
|
||||
let activeTab: Tab = "Overview";
|
||||
|
||||
// Hide Quotes tab if user lacks fetch permission
|
||||
$: visibleTabs = tabs.filter(
|
||||
(t) => t !== "Quotes" || permissions["sales.opportunity.quote.fetch"] !== false
|
||||
);
|
||||
|
||||
// Track whether ProductsTab is in edit mode
|
||||
let productsEditing = false;
|
||||
|
||||
@@ -126,7 +134,7 @@
|
||||
<!-- Mobile vertical nav menu -->
|
||||
{#if isMobile && mobileActiveTab === null}
|
||||
<div class="mobile-nav-menu">
|
||||
{#each tabs as tab}
|
||||
{#each visibleTabs as tab}
|
||||
<button
|
||||
class="mobile-nav-item"
|
||||
on:click={() => selectMobileTab(tab)}
|
||||
@@ -183,6 +191,22 @@
|
||||
d="M16 3.13a4 4 0 010 7.75"
|
||||
/>
|
||||
</svg>
|
||||
{:else if tab === "Quotes"}
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
width="20"
|
||||
height="20"
|
||||
>
|
||||
<path
|
||||
d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z"
|
||||
/>
|
||||
<polyline points="14 2 14 8 20 8" />
|
||||
<line x1="8" y1="13" x2="16" y2="13" />
|
||||
<line x1="8" y1="17" x2="13" y2="17" />
|
||||
</svg>
|
||||
{:else}
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
@@ -206,6 +230,9 @@
|
||||
{#if tab === "Contacts" && contacts.length > 0}
|
||||
<span class="mobile-nav-badge">{contacts.length}</span>
|
||||
{/if}
|
||||
{#if tab === "Quotes" && quotes.length > 0}
|
||||
<span class="mobile-nav-badge">{quotes.length}</span>
|
||||
{/if}
|
||||
<svg
|
||||
class="mobile-nav-chevron"
|
||||
viewBox="0 0 24 24"
|
||||
@@ -252,7 +279,7 @@
|
||||
{/if}
|
||||
|
||||
<div class="tab-bar" role="tablist">
|
||||
{#each tabs as tab}
|
||||
{#each visibleTabs as tab}
|
||||
<button
|
||||
class="tab-btn"
|
||||
class:active={activeTab === tab}
|
||||
@@ -270,6 +297,9 @@
|
||||
{#if tab === "Contacts" && contacts.length > 0}
|
||||
<span class="tab-count-badge">{contacts.length}</span>
|
||||
{/if}
|
||||
{#if tab === "Quotes" && quotes.length > 0}
|
||||
<span class="tab-count-badge">{quotes.length}</span>
|
||||
{/if}
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
@@ -293,6 +323,13 @@
|
||||
on:sequenceSaved={handleSequenceSaved}
|
||||
on:productsChanged={handleProductsChanged}
|
||||
/>
|
||||
{:else if activeTab === "Quotes"}
|
||||
<QuotesTab
|
||||
accessToken={data.accessToken}
|
||||
opportunityId={data.opportunityId}
|
||||
initialQuotes={quotes}
|
||||
{permissions}
|
||||
/>
|
||||
{:else if activeTab === "Notes"}
|
||||
<NotesTab
|
||||
{notes}
|
||||
|
||||
Reference in New Issue
Block a user