补齐平台订阅、外部登录、CRM 同步与用户端页面;Nginx 转发用户端接口,登录页使用 Google 与 Apple 标志。 Co-authored-by: Cursor <cursoragent@cursor.com>
889 行
20 KiB
Plaintext
889 行
20 KiB
Plaintext
<template>
|
|
<view class="workplace-page" :class="pageRtlClass" :style="pageTheme">
|
|
<view class="workplace-topbar">
|
|
<view class="workplace-identity" @click="handleSwitchCard">
|
|
<text class="identity-name">{{ activeCardName }}</text>
|
|
<text class="identity-switch">{{ t('workplace.switch_card') }}</text>
|
|
</view>
|
|
<view class="workplace-status-row">
|
|
<text v-if="showPublished" class="identity-badge">{{ t('workplace.published') }}</text>
|
|
<view class="radar-status-pill" :class="{ 'radar-status-idle': !state.radarOnline }" @click="handleOpenRadar">
|
|
<view v-if="state.radarOnline" class="radar-pulse-dot"></view>
|
|
<text class="radar-status-text">{{ radarStatusText }}</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<scroll-view class="workplace-scroll" :scroll-y="true">
|
|
<view v-if="state.errorMessage !== ''" class="workplace-error" @click="handleReload">
|
|
<text class="workplace-error-text">{{ state.errorMessage }}</text>
|
|
</view>
|
|
|
|
<view v-if="state.loading" class="workplace-skeleton">
|
|
<view class="skeleton-block"></view>
|
|
<view class="skeleton-line"></view>
|
|
<view class="skeleton-line"></view>
|
|
</view>
|
|
|
|
<view v-else class="workplace-body">
|
|
<view class="quick-showcase-section">
|
|
<text class="section-title">{{ t('workplace.quick_showcase') }}</text>
|
|
<view class="showcase-card">
|
|
<view class="showcase-qr-wrap" @click="handleOpenQrcode">
|
|
<rich-text
|
|
v-if="state.activeQrSvg !== ''"
|
|
class="showcase-qr-code"
|
|
:nodes="state.activeQrSvg"
|
|
></rich-text>
|
|
<text v-else class="showcase-qr-empty">{{ t('workplace.qr_empty') }}</text>
|
|
</view>
|
|
<view class="showcase-meta">
|
|
<text class="meta-title">{{ activeCardTitle }}</text>
|
|
<text v-if="activeCardCompany !== ''" class="meta-company">{{ activeCardCompany }}</text>
|
|
<text class="meta-preview-btn" @click="handlePreviewPortal">{{ t('workplace.view_portal') }}</text>
|
|
<view v-if="state.activeShareUrl !== ''" class="copy-link-btn" @click="handleCopyLink">
|
|
<text class="copy-link-text">{{ t('workplace.copy_link') }}</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="leads-todo-section">
|
|
<view class="section-header">
|
|
<text class="section-title">{{ t('workplace.leads_todo_title') }}</text>
|
|
<text class="section-action" @click="handleOpenLeads">{{ t('workplace.leads_view_all') }}</text>
|
|
</view>
|
|
<view v-if="state.recentLeads.length === 0" class="section-empty" @click="handleOpenLeads">
|
|
<text class="section-empty-text">{{ t('workplace.empty_leads') }}</text>
|
|
</view>
|
|
<view
|
|
v-for="lead in state.recentLeads"
|
|
:key="lead.id"
|
|
class="lead-item-card"
|
|
@click="handleOpenLead(lead.id)"
|
|
>
|
|
<view class="lead-top">
|
|
<text class="lead-name">{{ lead.name }}</text>
|
|
<text class="lead-status-chip" :class="'status-' + lead.status">{{ leadStatusLabel(lead.status) }}</text>
|
|
</view>
|
|
<text v-if="leadCompany(lead) !== ''" class="lead-company">{{ leadCompany(lead) }}</text>
|
|
<text v-if="leadNote(lead) !== ''" class="lead-notes" :lines="2">{{ leadNote(lead) }}</text>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="radar-pulse-section">
|
|
<view class="section-header">
|
|
<text class="section-title">{{ t('workplace.radar_pulse_title') }}</text>
|
|
<text class="section-action" @click="handleOpenRadar">{{ t('workplace.leads_view_all') }}</text>
|
|
</view>
|
|
<view v-if="state.hotRadarItem != null" class="pulse-card" @click="handleOpenRadar">
|
|
<view class="pulse-header">
|
|
<text class="pulse-place">{{ pulsePlace }}</text>
|
|
<text v-if="pulseScore !== ''" class="pulse-score-text">{{ pulseScore }}</text>
|
|
</view>
|
|
<text class="pulse-desc">{{ pulseDetail }}</text>
|
|
<view class="pulse-footer">
|
|
<text class="pulse-time">{{ formatTime(state.hotRadarItem!.createTime) }}</text>
|
|
<view class="pulse-action-btn" @click.stop="handleQuickContact">
|
|
<text class="pulse-action-text">{{ t('workplace.radar_action_whatsapp') }}</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
<view v-else class="section-empty" @click="handleOpenRadar">
|
|
<text class="section-empty-text">{{ t('workplace.empty_radar') }}</text>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="kpi-grid-section">
|
|
<view class="section-header">
|
|
<text class="section-title">{{ t('workplace.kpi_title') }}</text>
|
|
</view>
|
|
<view class="kpi-grid">
|
|
<view class="kpi-card" @click="handleOpenRadar">
|
|
<text class="kpi-label">{{ t('workplace.kpi_taps') }}</text>
|
|
<view class="kpi-value-row">
|
|
<text class="kpi-value">{{ state.kpis.tapCount }}</text>
|
|
<text v-if="state.kpis.tapDelta > 0" class="kpi-delta">+{{ state.kpis.tapDelta }}</text>
|
|
</view>
|
|
</view>
|
|
<view class="kpi-card" @click="handleOpenRadar">
|
|
<text class="kpi-label">{{ t('workplace.kpi_visitors') }}</text>
|
|
<view class="kpi-value-row">
|
|
<text class="kpi-value">{{ state.kpis.viewCount }}</text>
|
|
<text v-if="state.kpis.viewDelta > 0" class="kpi-delta">+{{ state.kpis.viewDelta }}</text>
|
|
</view>
|
|
</view>
|
|
<view class="kpi-card" @click="handleOpenLeads">
|
|
<text class="kpi-label">{{ t('workplace.kpi_leads') }}</text>
|
|
<view class="kpi-value-row">
|
|
<text class="kpi-value">{{ state.kpis.newLeadCount }}</text>
|
|
<text v-if="state.kpis.newLeadDelta > 0" class="kpi-delta">+{{ state.kpis.newLeadDelta }}</text>
|
|
</view>
|
|
</view>
|
|
<view class="kpi-card" @click="handleOpenLeads">
|
|
<text class="kpi-label">{{ t('workplace.kpi_conversion') }}</text>
|
|
<view class="kpi-value-row">
|
|
<text class="kpi-value">{{ conversionText }}</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="workplace-spacer"></view>
|
|
</scroll-view>
|
|
</view>
|
|
</template>
|
|
|
|
<script setup lang="uts">
|
|
import { getCurrentLocale, isRtl, OmniLocale } from '../../../common/locale.uts';
|
|
import { utsLightColors } from '../../../common/tokens.uts';
|
|
import { LeadEntity } from '../../../common/types.uts';
|
|
import {
|
|
createWorkplaceState,
|
|
loadWorkplaceData,
|
|
openCardPreview,
|
|
openCardQrcode,
|
|
openCardsPage,
|
|
openLeadDetail,
|
|
openLeadsPage,
|
|
openRadarPage,
|
|
resolveWorkplaceText,
|
|
WorkplaceState,
|
|
} from './useWorkplace.uts';
|
|
|
|
const state = reactive(createWorkplaceState());
|
|
|
|
const currentLang = computed((): OmniLocale => getCurrentLocale());
|
|
const pageRtlClass = computed((): string => (isRtl(currentLang.value) ? 'rtl-layout' : 'ltr-layout'));
|
|
const pageTheme = computed((): Record<string, string> => {
|
|
return {
|
|
'--primary': utsLightColors.primary,
|
|
'--bg': utsLightColors.bg,
|
|
'--surface': utsLightColors.surface,
|
|
'--text': utsLightColors.text,
|
|
'--text2': utsLightColors.text2,
|
|
'--border': utsLightColors.border,
|
|
'--primary-soft': utsLightColors.primarySoft,
|
|
'--error': utsLightColors.error,
|
|
};
|
|
});
|
|
|
|
function t(key: string): string {
|
|
return resolveWorkplaceText(key, currentLang.value);
|
|
}
|
|
|
|
const activeCardName = computed((): string => {
|
|
if (state.activeCard == null || state.activeCard!.name === '') {
|
|
return t('workplace.empty_card');
|
|
}
|
|
return state.activeCard!.name;
|
|
});
|
|
|
|
const activeCardTitle = computed((): string => {
|
|
if (state.activeCard == null || state.activeCard!.title == null) {
|
|
return '';
|
|
}
|
|
return state.activeCard!.title!;
|
|
});
|
|
|
|
const activeCardCompany = computed((): string => {
|
|
if (state.activeCard == null || state.activeCard!.company == null) {
|
|
return '';
|
|
}
|
|
return state.activeCard!.company!;
|
|
});
|
|
|
|
const showPublished = computed((): boolean => {
|
|
return state.activeCard != null && state.activeCard!.status === 'published';
|
|
});
|
|
|
|
const radarStatusText = computed((): string => {
|
|
if (state.radarOnline) {
|
|
return t('workplace.radar_active');
|
|
}
|
|
return t('workplace.radar_pulse_title');
|
|
});
|
|
|
|
const conversionText = computed((): string => {
|
|
return String(state.kpis.conversionRate) + '%';
|
|
});
|
|
|
|
const pulsePlace = computed((): string => {
|
|
if (state.hotRadarItem == null) {
|
|
return '';
|
|
}
|
|
const country = state.hotRadarItem!.country != null ? state.hotRadarItem!.country : '';
|
|
const city = state.hotRadarItem!.city != null ? state.hotRadarItem!.city : '';
|
|
if (city !== '' && country !== '') {
|
|
return city + ' · ' + country;
|
|
}
|
|
if (city !== '') {
|
|
return city;
|
|
}
|
|
return country;
|
|
});
|
|
|
|
const pulseScore = computed((): string => {
|
|
if (state.hotRadarItem == null || state.hotRadarItem!.intentScore == null) {
|
|
return '';
|
|
}
|
|
const score = state.hotRadarItem!.intentScore!;
|
|
if (score <= 0) {
|
|
return '';
|
|
}
|
|
return t('workplace.score') + ' ' + String(score);
|
|
});
|
|
|
|
const pulseDetail = computed((): string => {
|
|
if (state.hotRadarItem == null) {
|
|
return '';
|
|
}
|
|
return formatStay(state.hotRadarItem!.stayDurationMs);
|
|
});
|
|
|
|
function isZh(): boolean {
|
|
return currentLang.value.startsWith('zh');
|
|
}
|
|
|
|
function formatStay(ms: number): string {
|
|
if (ms >= 60000) {
|
|
const minutes = Math.floor(ms / 60000);
|
|
return isZh() ? String(minutes) + ' 分钟' : String(minutes) + ' min';
|
|
}
|
|
const seconds = Math.max(0, Math.floor(ms / 1000));
|
|
return String(seconds) + 's';
|
|
}
|
|
|
|
function formatTime(time: string | Date | undefined): string {
|
|
if (time == null) {
|
|
return '';
|
|
}
|
|
let ms = 0;
|
|
if (typeof time === 'string') {
|
|
const parsed = Date.parse(time);
|
|
if (!Number.isNaN(parsed)) {
|
|
ms = parsed;
|
|
}
|
|
} else {
|
|
ms = time.getTime();
|
|
}
|
|
if (ms <= 0) {
|
|
return '';
|
|
}
|
|
const diffMin = Math.floor(Math.max(0, Date.now() - ms) / 60000);
|
|
if (diffMin < 2) {
|
|
return isZh() ? '刚刚' : 'Just now';
|
|
}
|
|
if (diffMin < 60) {
|
|
return isZh() ? String(diffMin) + ' 分钟前' : String(diffMin) + 'm ago';
|
|
}
|
|
const diffHour = Math.floor(diffMin / 60);
|
|
if (diffHour < 24) {
|
|
return isZh() ? String(diffHour) + ' 小时前' : String(diffHour) + 'h ago';
|
|
}
|
|
const diffDay = Math.floor(diffHour / 24);
|
|
return isZh() ? String(diffDay) + ' 天前' : String(diffDay) + 'd ago';
|
|
}
|
|
|
|
function leadStatusLabel(status: string): string {
|
|
if (status === 'new') {
|
|
return isZh() ? '新线索' : 'New';
|
|
}
|
|
if (status === 'contacted') {
|
|
return isZh() ? '已联系' : 'Contacted';
|
|
}
|
|
if (status === 'hot') {
|
|
return isZh() ? '高意向' : 'High intent';
|
|
}
|
|
if (status === 'won') {
|
|
return isZh() ? '已成交' : 'Won';
|
|
}
|
|
if (status === 'archived') {
|
|
return isZh() ? '已归档' : 'Archived';
|
|
}
|
|
return status;
|
|
}
|
|
|
|
function leadCompany(lead: LeadEntity): string {
|
|
if (lead.company == null) {
|
|
return '';
|
|
}
|
|
return lead.company;
|
|
}
|
|
|
|
function leadNote(lead: LeadEntity): string {
|
|
if (lead.notes == null) {
|
|
return '';
|
|
}
|
|
return lead.notes;
|
|
}
|
|
|
|
function handleOpenQrcode(): void {
|
|
if (state.activeCard != null) {
|
|
openCardQrcode(state.activeCard!);
|
|
}
|
|
}
|
|
|
|
function handlePreviewPortal(): void {
|
|
if (state.activeCard != null) {
|
|
openCardPreview(state.activeCard!);
|
|
}
|
|
}
|
|
|
|
function handleSwitchCard(): void {
|
|
openCardsPage();
|
|
}
|
|
|
|
function handleOpenRadar(): void {
|
|
openRadarPage();
|
|
}
|
|
|
|
function handleOpenLeads(): void {
|
|
openLeadsPage();
|
|
}
|
|
|
|
function handleOpenLead(id: string | number): void {
|
|
openLeadDetail(id);
|
|
}
|
|
|
|
function handleQuickContact(): void {
|
|
openRadarPage();
|
|
}
|
|
|
|
function handleCopyLink(): void {
|
|
if (state.activeShareUrl === '') {
|
|
return;
|
|
}
|
|
const url = state.activeShareUrl;
|
|
uni.setClipboardData({
|
|
data: url,
|
|
success: () => {
|
|
uni.showToast({
|
|
title: t('workplace.link_copied'),
|
|
icon: 'none',
|
|
});
|
|
},
|
|
});
|
|
}
|
|
|
|
function handleReload(): void {
|
|
loadWorkplaceData(state);
|
|
}
|
|
|
|
onShow(() => {
|
|
loadWorkplaceData(state);
|
|
});
|
|
</script>
|
|
|
|
<style scoped>
|
|
.workplace-page {
|
|
display: flex;
|
|
flex-direction: column;
|
|
width: 100vw;
|
|
max-width: 480px;
|
|
height: 100vh;
|
|
margin-inline: auto;
|
|
background-color: var(--bg, #F5F8FC);
|
|
color: var(--text, #101828);
|
|
overflow: hidden;
|
|
}
|
|
|
|
.rtl-layout {
|
|
direction: rtl;
|
|
}
|
|
|
|
.workplace-topbar {
|
|
flex-shrink: 0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
padding-block-start: calc(8px + env(safe-area-inset-top, 0px));
|
|
padding-inline: 16px;
|
|
padding-block-end: 8px;
|
|
background-color: #F5F8FC;
|
|
}
|
|
|
|
.workplace-identity {
|
|
min-height: 44px;
|
|
display: flex;
|
|
flex-direction: row;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
}
|
|
|
|
.workplace-status-row {
|
|
display: flex;
|
|
flex-direction: row;
|
|
align-items: center;
|
|
margin-block-end: 4px;
|
|
}
|
|
|
|
.workplace-identity:active {
|
|
opacity: 0.72;
|
|
}
|
|
|
|
.identity-name {
|
|
flex: 1;
|
|
min-width: 0;
|
|
font-size: 20px;
|
|
font-weight: 700;
|
|
line-height: 1.25;
|
|
color: #101828;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.identity-badge {
|
|
flex-shrink: 0;
|
|
margin-inline: 8px;
|
|
padding: 2px 8px;
|
|
border-radius: 9999px;
|
|
background-color: #EBF5FF;
|
|
font-size: 12px;
|
|
font-weight: 600;
|
|
color: #0B5CFF;
|
|
}
|
|
|
|
.identity-switch {
|
|
flex-shrink: 0;
|
|
min-height: 44px;
|
|
padding-inline: 4px;
|
|
display: flex;
|
|
align-items: center;
|
|
font-size: 14px;
|
|
font-weight: 600;
|
|
color: #0B5CFF;
|
|
}
|
|
|
|
.radar-status-pill {
|
|
flex-shrink: 0;
|
|
min-height: 44px;
|
|
padding-inline: 10px;
|
|
display: flex;
|
|
flex-direction: row;
|
|
align-items: center;
|
|
border-radius: 9999px;
|
|
background-color: #ECFDF3;
|
|
}
|
|
|
|
.radar-status-idle {
|
|
background-color: #F7F8FA;
|
|
}
|
|
|
|
.radar-status-idle .radar-status-text {
|
|
color: #64748B;
|
|
}
|
|
|
|
.radar-status-pill:active {
|
|
opacity: 0.72;
|
|
}
|
|
|
|
.radar-pulse-dot {
|
|
width: 8px;
|
|
height: 8px;
|
|
margin-inline-end: 6px;
|
|
border-radius: 4px;
|
|
background-color: #12B76A;
|
|
}
|
|
|
|
.radar-status-text {
|
|
font-size: 12px;
|
|
font-weight: 600;
|
|
line-height: 1.4;
|
|
color: #027A48;
|
|
}
|
|
|
|
.workplace-scroll {
|
|
flex: 1;
|
|
width: 100%;
|
|
height: 0;
|
|
min-height: 0;
|
|
}
|
|
|
|
.workplace-body {
|
|
width: 100%;
|
|
box-sizing: border-box;
|
|
padding-inline: 16px;
|
|
}
|
|
|
|
.workplace-error {
|
|
margin: 0 16px 8px;
|
|
padding: 12px;
|
|
border-radius: 12px;
|
|
background-color: #FEF3F2;
|
|
}
|
|
|
|
.workplace-error-text {
|
|
font-size: 14px;
|
|
line-height: 1.5;
|
|
color: #B42318;
|
|
}
|
|
|
|
.workplace-skeleton {
|
|
padding: 8px 16px;
|
|
}
|
|
|
|
.skeleton-block {
|
|
height: 112px;
|
|
margin-block-end: 12px;
|
|
border-radius: 16px;
|
|
background-color: #E3E7ED;
|
|
}
|
|
|
|
.skeleton-line {
|
|
height: 72px;
|
|
margin-block-end: 12px;
|
|
border-radius: 16px;
|
|
background-color: #E3E7ED;
|
|
}
|
|
|
|
.section-header {
|
|
display: flex;
|
|
flex-direction: row;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
min-height: 44px;
|
|
}
|
|
|
|
.section-title {
|
|
font-size: 16px;
|
|
font-weight: 700;
|
|
line-height: 1.25;
|
|
color: #101828;
|
|
}
|
|
|
|
.section-action {
|
|
min-height: 44px;
|
|
padding-inline: 4px;
|
|
display: flex;
|
|
align-items: center;
|
|
font-size: 14px;
|
|
font-weight: 600;
|
|
color: #0B5CFF;
|
|
}
|
|
|
|
.section-empty {
|
|
min-height: 44px;
|
|
margin-block-end: 8px;
|
|
padding: 14px;
|
|
display: flex;
|
|
align-items: center;
|
|
background-color: #FFFFFF;
|
|
border: 1px solid #E3E7ED;
|
|
border-radius: 16px;
|
|
}
|
|
|
|
.section-empty:active {
|
|
background-color: #F7F8FA;
|
|
}
|
|
|
|
.section-empty-text {
|
|
font-size: 14px;
|
|
line-height: 1.5;
|
|
color: #64748B;
|
|
}
|
|
|
|
.quick-showcase-section {
|
|
margin-block-end: 8px;
|
|
}
|
|
|
|
.showcase-card {
|
|
display: flex;
|
|
flex-direction: row;
|
|
align-items: center;
|
|
margin-top: 4px;
|
|
padding: 12px;
|
|
background-color: #FFFFFF;
|
|
border: 1px solid #E3E7ED;
|
|
border-radius: 16px;
|
|
}
|
|
|
|
.showcase-qr-wrap {
|
|
width: 104px;
|
|
height: 104px;
|
|
flex-shrink: 0;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
background-color: #FFFFFF;
|
|
}
|
|
|
|
.showcase-qr-wrap:active {
|
|
opacity: 0.8;
|
|
}
|
|
|
|
.showcase-qr-code {
|
|
width: 104px;
|
|
height: 104px;
|
|
}
|
|
|
|
.showcase-qr-empty {
|
|
font-size: 12px;
|
|
line-height: 1.4;
|
|
color: #64748B;
|
|
text-align: center;
|
|
}
|
|
|
|
.showcase-meta {
|
|
flex: 1;
|
|
min-width: 0;
|
|
margin-inline-start: 12px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.meta-title {
|
|
font-size: 16px;
|
|
font-weight: 700;
|
|
line-height: 1.3;
|
|
color: #101828;
|
|
}
|
|
|
|
.meta-company {
|
|
margin-top: 2px;
|
|
font-size: 13px;
|
|
line-height: 1.4;
|
|
color: #64748B;
|
|
}
|
|
|
|
.meta-preview-btn {
|
|
min-height: 44px;
|
|
display: flex;
|
|
align-items: center;
|
|
font-size: 14px;
|
|
font-weight: 600;
|
|
color: #0B5CFF;
|
|
}
|
|
|
|
.copy-link-btn {
|
|
align-self: flex-start;
|
|
min-height: 44px;
|
|
padding: 0 12px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
background-color: #EBF5FF;
|
|
border-radius: 9999px;
|
|
}
|
|
|
|
.copy-link-btn:active {
|
|
opacity: 0.72;
|
|
}
|
|
|
|
.copy-link-text {
|
|
font-size: 13px;
|
|
font-weight: 700;
|
|
color: #0B5CFF;
|
|
}
|
|
|
|
.leads-todo-section,
|
|
.radar-pulse-section,
|
|
.kpi-grid-section {
|
|
margin-block-start: 8px;
|
|
}
|
|
|
|
.lead-item-card {
|
|
margin-block-end: 8px;
|
|
padding: 12px 14px;
|
|
background-color: #FFFFFF;
|
|
border: 1px solid #E3E7ED;
|
|
border-radius: 16px;
|
|
}
|
|
|
|
.lead-item-card:active {
|
|
background-color: #F7F8FA;
|
|
}
|
|
|
|
.lead-top {
|
|
display: flex;
|
|
flex-direction: row;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
}
|
|
|
|
.lead-name {
|
|
flex: 1;
|
|
min-width: 0;
|
|
font-size: 16px;
|
|
font-weight: 700;
|
|
line-height: 1.3;
|
|
color: #101828;
|
|
}
|
|
|
|
.lead-status-chip {
|
|
flex-shrink: 0;
|
|
margin-inline-start: 8px;
|
|
padding: 2px 8px;
|
|
border-radius: 9999px;
|
|
font-size: 12px;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.status-new,
|
|
.status-hot {
|
|
color: #B42318;
|
|
background-color: #FEF3F2;
|
|
}
|
|
|
|
.status-contacted {
|
|
color: #B54708;
|
|
background-color: #FFFAEB;
|
|
}
|
|
|
|
.status-won {
|
|
color: #027A48;
|
|
background-color: #ECFDF3;
|
|
}
|
|
|
|
.status-archived {
|
|
color: #64748B;
|
|
background-color: #F7F8FA;
|
|
}
|
|
|
|
.lead-company {
|
|
margin-top: 4px;
|
|
font-size: 14px;
|
|
line-height: 1.4;
|
|
color: #101828;
|
|
}
|
|
|
|
.lead-notes {
|
|
margin-top: 2px;
|
|
max-height: 39px;
|
|
font-size: 13px;
|
|
line-height: 1.5;
|
|
color: #64748B;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
.pulse-card {
|
|
margin-block-end: 8px;
|
|
padding: 14px;
|
|
background-color: #FFFFFF;
|
|
border: 1px solid #E3E7ED;
|
|
border-radius: 16px;
|
|
}
|
|
|
|
.pulse-card:active {
|
|
background-color: #F7F8FA;
|
|
}
|
|
|
|
.pulse-header {
|
|
display: flex;
|
|
flex-direction: row;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
}
|
|
|
|
.pulse-place {
|
|
flex: 1;
|
|
min-width: 0;
|
|
font-size: 16px;
|
|
font-weight: 700;
|
|
line-height: 1.3;
|
|
color: #101828;
|
|
}
|
|
|
|
.pulse-score-text {
|
|
flex-shrink: 0;
|
|
margin-inline-start: 8px;
|
|
font-size: 12px;
|
|
font-weight: 700;
|
|
color: #B42318;
|
|
}
|
|
|
|
.pulse-desc {
|
|
margin-top: 4px;
|
|
font-size: 14px;
|
|
line-height: 1.5;
|
|
color: #64748B;
|
|
}
|
|
|
|
.pulse-footer {
|
|
display: flex;
|
|
flex-direction: row;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
margin-top: 8px;
|
|
}
|
|
|
|
.pulse-time {
|
|
font-size: 12px;
|
|
color: #64748B;
|
|
}
|
|
|
|
.pulse-action-btn {
|
|
min-height: 44px;
|
|
padding-inline: 14px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
background-color: #0B5CFF;
|
|
border-radius: 9999px;
|
|
}
|
|
|
|
.pulse-action-btn:active {
|
|
opacity: 0.88;
|
|
}
|
|
|
|
.pulse-action-text {
|
|
font-size: 14px;
|
|
font-weight: 600;
|
|
color: #FFFFFF;
|
|
}
|
|
|
|
.kpi-grid {
|
|
display: flex;
|
|
flex-direction: row;
|
|
flex-wrap: wrap;
|
|
justify-content: space-between;
|
|
}
|
|
|
|
.kpi-card {
|
|
width: 48%;
|
|
box-sizing: border-box;
|
|
margin-block-end: 8px;
|
|
padding: 12px;
|
|
background-color: #FFFFFF;
|
|
border: 1px solid #E3E7ED;
|
|
border-radius: 16px;
|
|
}
|
|
|
|
.kpi-card:active {
|
|
opacity: 0.8;
|
|
}
|
|
|
|
.kpi-label {
|
|
font-size: 12px;
|
|
line-height: 1.4;
|
|
color: #64748B;
|
|
}
|
|
|
|
.kpi-value-row {
|
|
display: flex;
|
|
flex-direction: row;
|
|
align-items: baseline;
|
|
margin-top: 4px;
|
|
}
|
|
|
|
.kpi-value {
|
|
font-size: 22px;
|
|
font-weight: 700;
|
|
line-height: 1.2;
|
|
color: #101828;
|
|
}
|
|
|
|
.kpi-delta {
|
|
margin-inline-start: 6px;
|
|
font-size: 12px;
|
|
font-weight: 600;
|
|
color: #027A48;
|
|
}
|
|
|
|
.workplace-spacer {
|
|
height: calc(16px + var(--window-bottom, 0px) + env(safe-area-inset-bottom, 0px));
|
|
}
|
|
</style>
|