feat: add workflow actions, admin enhancements, and comprehensive test coverage
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { onDestroy, onMount } from "svelte";
|
||||
import { createEventDispatcher, onDestroy, onMount } from "svelte";
|
||||
import { PUBLIC_API_URL } from "$env/static/public";
|
||||
import { io, type Socket } from "socket.io-client";
|
||||
import {
|
||||
@@ -14,11 +14,17 @@
|
||||
export let opportunityId: string = "";
|
||||
export let quotePreviewPdfUrl: string | null = null;
|
||||
export let initialQuotes: CommittedQuote[] = [];
|
||||
export let initialQuoteId: string | null = null;
|
||||
export let permissions: PermissionMap = {} as PermissionMap;
|
||||
export let isClosedOpportunity: boolean = false;
|
||||
|
||||
const dispatch = createEventDispatcher<{ quotesChanged: CommittedQuote[] }>();
|
||||
|
||||
// ── Permission helpers ──
|
||||
$: canFetchQuotes = permissions["sales.opportunity.quote.fetch"] !== false;
|
||||
$: canCommitQuote = permissions["sales.opportunity.quote.commit"] === true;
|
||||
$: canCommitQuote =
|
||||
!isClosedOpportunity &&
|
||||
permissions["sales.opportunity.quote.commit"] === true;
|
||||
$: canPreviewQuote = permissions["sales.opportunity.quote.preview"] === true;
|
||||
$: canDownloadQuote =
|
||||
permissions["sales.opportunity.quote.download"] === true;
|
||||
@@ -29,8 +35,35 @@
|
||||
let quotes: CommittedQuote[] = initialQuotes;
|
||||
let quotesLoading = false;
|
||||
let quotesError = "";
|
||||
|
||||
// Determine initial selection: prefer initialQuoteId match, fall back to first quote
|
||||
const initialMatch = initialQuoteId
|
||||
? initialQuotes.find(
|
||||
(q) => q.id === initialQuoteId || q.quoteFileName === initialQuoteId,
|
||||
)
|
||||
: null;
|
||||
console.log(
|
||||
"[QuotesTab] initialQuoteId:",
|
||||
initialQuoteId,
|
||||
"quotes:",
|
||||
initialQuotes.map((q) => ({ id: q.id, fileName: q.quoteFileName })),
|
||||
"match:",
|
||||
initialMatch?.id,
|
||||
);
|
||||
let selectedQuote: CommittedQuote | null =
|
||||
initialQuotes.length > 0 ? initialQuotes[0] : null;
|
||||
initialMatch ?? (initialQuotes.length > 0 ? initialQuotes[0] : null);
|
||||
|
||||
// Auto-select quote by ID when navigating from activity tab (for post-mount updates)
|
||||
$: if (initialQuoteId && quotes.length > 0) {
|
||||
const match = quotes.find(
|
||||
(q) => q.id === initialQuoteId || q.quoteFileName === initialQuoteId,
|
||||
);
|
||||
if (match) {
|
||||
selectedQuote = match;
|
||||
viewMode = "list";
|
||||
loadQuotePreview(match.id);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Detail data (lazy-loaded with regen data & params) ──
|
||||
let detailQuotes: Map<string, CommittedQuote> = new Map();
|
||||
@@ -145,6 +178,7 @@
|
||||
try {
|
||||
const result = await sales.fetchQuotes(accessToken, opportunityId);
|
||||
quotes = result.data ?? [];
|
||||
dispatch("quotesChanged", quotes);
|
||||
if (quotes.length > 0 && !selectedQuote) {
|
||||
selectedQuote = quotes[0];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user