Rename compose file to docker-compose.yml
This commit is contained in:
@@ -1487,6 +1487,7 @@ const peopleSortOptions: Array<{ value: PeopleSortMode; label: string }> = [
|
||||
{ value: "country", label: "Country" },
|
||||
];
|
||||
const selectedDealId = ref(deals.value[0]?.id ?? "");
|
||||
const selectedDealStepsExpanded = ref(false);
|
||||
|
||||
const commThreads = computed(() => {
|
||||
const sorted = [...commItems.value].sort((a, b) => a.at.localeCompare(b.at));
|
||||
@@ -1803,6 +1804,22 @@ function formatDealHeadline(deal: Deal) {
|
||||
return `${title} за ${amountRaw}`;
|
||||
}
|
||||
|
||||
function getDealCurrentStep(deal: Deal) {
|
||||
if (!deal.steps?.length) return null;
|
||||
if (deal.currentStepId) {
|
||||
const explicit = deal.steps.find((step) => step.id === deal.currentStepId);
|
||||
if (explicit) return explicit;
|
||||
}
|
||||
const inProgress = deal.steps.find((step) => step.status === "in_progress");
|
||||
if (inProgress) return inProgress;
|
||||
const nextTodo = deal.steps.find((step) => step.status !== "done");
|
||||
return nextTodo ?? deal.steps[deal.steps.length - 1];
|
||||
}
|
||||
|
||||
function getDealCurrentStepLabel(deal: Deal) {
|
||||
return getDealCurrentStep(deal)?.title?.trim() || deal.nextStep.trim() || deal.stage.trim() || "Без шага";
|
||||
}
|
||||
|
||||
function parseDateFromText(input: string) {
|
||||
const text = input.trim();
|
||||
if (!text) return null;
|
||||
@@ -1847,11 +1864,33 @@ function formatDealDeadline(dueDate: Date) {
|
||||
return `через ${dayDiff} ${pluralizeRuDays(dayDiff)}`;
|
||||
}
|
||||
|
||||
function isDealStepDone(step: DealStep) {
|
||||
return step.status === "done";
|
||||
}
|
||||
|
||||
function formatDealStepMeta(step: DealStep) {
|
||||
if (step.status === "done") return "выполнено";
|
||||
if (step.status === "blocked") return "заблокировано";
|
||||
if (!step.dueAt) {
|
||||
if (step.status === "in_progress") return "в работе";
|
||||
return "без дедлайна";
|
||||
}
|
||||
const parsed = new Date(step.dueAt);
|
||||
if (Number.isNaN(parsed.getTime())) return "без дедлайна";
|
||||
return formatDealDeadline(parsed);
|
||||
}
|
||||
|
||||
const selectedWorkspaceDealDueDate = computed(() => {
|
||||
const deal = selectedWorkspaceDeal.value;
|
||||
if (!deal) return null;
|
||||
|
||||
const fromNextStep = parseDateFromText(deal.nextStep);
|
||||
const currentStep = getDealCurrentStep(deal);
|
||||
if (currentStep?.dueAt) {
|
||||
const parsed = new Date(currentStep.dueAt);
|
||||
if (!Number.isNaN(parsed.getTime())) return parsed;
|
||||
}
|
||||
|
||||
const fromNextStep = parseDateFromText(currentStep?.title || deal.nextStep);
|
||||
if (fromNextStep) return fromNextStep;
|
||||
|
||||
const now = Date.now();
|
||||
@@ -1870,12 +1909,25 @@ const selectedWorkspaceDealDueDate = computed(() => {
|
||||
const selectedWorkspaceDealSubtitle = computed(() => {
|
||||
const deal = selectedWorkspaceDeal.value;
|
||||
if (!deal) return "";
|
||||
const stepLabel = deal.nextStep.trim() || deal.stage.trim() || "Без шага";
|
||||
const stepLabel = getDealCurrentStepLabel(deal);
|
||||
const dueDate = selectedWorkspaceDealDueDate.value;
|
||||
if (!dueDate) return `${stepLabel} · без дедлайна`;
|
||||
return `${stepLabel} · ${formatDealDeadline(dueDate)}`;
|
||||
});
|
||||
|
||||
const selectedWorkspaceDealSteps = computed(() => {
|
||||
const deal = selectedWorkspaceDeal.value;
|
||||
if (!deal?.steps?.length) return [];
|
||||
return [...deal.steps].sort((a, b) => a.order - b.order);
|
||||
});
|
||||
|
||||
watch(
|
||||
() => selectedWorkspaceDeal.value?.id ?? "",
|
||||
() => {
|
||||
selectedDealStepsExpanded.value = false;
|
||||
},
|
||||
);
|
||||
|
||||
async function transcribeCallItem(item: CommItem) {
|
||||
const itemId = item.id;
|
||||
if (callTranscriptLoading.value[itemId]) return;
|
||||
@@ -1964,6 +2016,7 @@ function pushPilotNote(text: string) {
|
||||
function openCommunicationThread(contact: string) {
|
||||
selectedTab.value = "communications";
|
||||
peopleLeftMode.value = "contacts";
|
||||
selectedDealStepsExpanded.value = false;
|
||||
const linkedContact = contacts.value.find((item) => item.name === contact);
|
||||
if (linkedContact) {
|
||||
selectedContactId.value = linkedContact.id;
|
||||
@@ -1980,6 +2033,7 @@ function openCommunicationThread(contact: string) {
|
||||
|
||||
function openDealThread(deal: Deal) {
|
||||
selectedDealId.value = deal.id;
|
||||
selectedDealStepsExpanded.value = false;
|
||||
openCommunicationThread(deal.contact);
|
||||
}
|
||||
|
||||
@@ -2955,7 +3009,7 @@ async function decideFeedCard(card: FeedCard, decision: "accepted" | "rejected")
|
||||
<span class="shrink-0 text-[10px] text-base-content/55">{{ deal.amount }}</span>
|
||||
</div>
|
||||
<p class="mt-0.5 truncate text-[11px] text-base-content/75">{{ deal.company }} · {{ deal.stage }}</p>
|
||||
<p class="mt-0.5 truncate text-[11px] text-base-content/60">{{ deal.nextStep }}</p>
|
||||
<p class="mt-0.5 truncate text-[11px] text-base-content/60">{{ getDealCurrentStepLabel(deal) }}</p>
|
||||
</button>
|
||||
|
||||
<p v-if="peopleListMode === 'contacts' && peopleContactList.length === 0" class="px-1 py-2 text-xs text-base-content/55">
|
||||
@@ -3421,6 +3475,33 @@ async function decideFeedCard(card: FeedCard, decision: "accepted" | "rejected")
|
||||
<p class="mt-1 text-[11px] text-base-content/75">
|
||||
{{ selectedWorkspaceDealSubtitle }}
|
||||
</p>
|
||||
<button
|
||||
v-if="selectedWorkspaceDealSteps.length"
|
||||
class="mt-2 text-[11px] font-medium text-primary hover:underline"
|
||||
@click="selectedDealStepsExpanded = !selectedDealStepsExpanded"
|
||||
>
|
||||
{{ selectedDealStepsExpanded ? "Скрыть шаги" : `Показать шаги (${selectedWorkspaceDealSteps.length})` }}
|
||||
</button>
|
||||
<div v-if="selectedDealStepsExpanded && selectedWorkspaceDealSteps.length" class="mt-2 space-y-1.5">
|
||||
<div
|
||||
v-for="step in selectedWorkspaceDealSteps"
|
||||
:key="step.id"
|
||||
class="flex items-start gap-2 rounded-lg border border-base-300/70 bg-base-100/80 px-2 py-1.5"
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="checkbox checkbox-xs mt-0.5"
|
||||
:checked="isDealStepDone(step)"
|
||||
disabled
|
||||
>
|
||||
<div class="min-w-0 flex-1">
|
||||
<p class="truncate text-[11px] font-medium" :class="isDealStepDone(step) ? 'line-through text-base-content/60' : 'text-base-content/90'">
|
||||
{{ step.title }}
|
||||
</p>
|
||||
<p class="mt-0.5 text-[10px] text-base-content/55">{{ formatDealStepMeta(step) }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
||||
Reference in New Issue
Block a user