diff --git a/cmd/server/main.go b/cmd/server/main.go index 774642b..89bd4b1 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -256,6 +256,15 @@ func main() { } }) + // Get unique customer list from trial periods + http.HandleFunc("/api/trial-customers/list", func(w http.ResponseWriter, r *http.Request) { + if r.Method == "GET" { + trialPeriodHandler.GetTrialCustomerList(w, r) + } else { + http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) + } + }) + http.HandleFunc("/api/trial-periods/", func(w http.ResponseWriter, r *http.Request) { path := r.URL.Path diff --git a/frontend/css/style.css b/frontend/css/style.css index 036a75b..34ae48a 100644 --- a/frontend/css/style.css +++ b/frontend/css/style.css @@ -120,6 +120,45 @@ body { overflow-y: auto; } +/* Navigation Group Styles */ +.nav-group { + margin-bottom: 10px; +} + +.nav-group-title { + display: flex; + align-items: center; + padding: 12px 20px; + color: rgba(255, 255, 255, 0.5); + font-size: 0.75rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 1px; + gap: 10px; +} + +.nav-group-title i { + font-size: 0.9rem; + color: var(--primary-orange); +} + +.nav-group .nav-item { + padding-left: 35px; +} + +.sidebar.collapsed .nav-group-title span { + display: none; +} + +.sidebar.collapsed .nav-group-title { + justify-content: center; + padding: 12px; +} + +.sidebar.collapsed .nav-group .nav-item { + padding-left: 15px; +} + .nav-item { display: flex; align-items: center; @@ -1401,4 +1440,726 @@ td.overflow-cell { .pagination-info { text-align: center; } +} + +/* Status Badge Styles */ +.status-badge { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 5px 12px; + border-radius: 20px; + font-size: 0.8rem; + font-weight: 600; + white-space: nowrap; +} + +.status-badge i { + font-size: 0.75rem; +} + +.status-active { + background-color: #d4edda; + color: #155724; + border: 1px solid #c3e6cb; +} + +.status-inactive { + background-color: #e2e3e5; + color: #383d41; + border: 1px solid #d6d8db; +} + +.status-expired { + background-color: #f8d7da; + color: #721c24; + border: 1px solid #f5c6cb; +} + +.status-urgent { + background-color: #f8d7da; + color: #721c24; + border: 1px solid #f5c6cb; + animation: pulse 1.5s infinite; +} + +.status-warning { + background-color: #fff3cd; + color: #856404; + border: 1px solid #ffeeba; +} + +.status-notice { + background-color: #cce5ff; + color: #004085; + border: 1px solid #b8daff; +} + +@keyframes pulse { + + 0%, + 100% { + opacity: 1; + } + + 50% { + opacity: 0.7; + } +} + +/* Progress Status Badges */ +.progress-badge { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 4px 10px; + border-radius: 15px; + font-size: 0.75rem; + font-weight: 600; +} + +.progress-completed { + background-color: #d4edda; + color: #155724; +} + +.progress-in-progress { + background-color: #cce5ff; + color: #004085; +} + +.progress-pending { + background-color: #fff3cd; + color: #856404; +} + +.progress-rejected { + background-color: #f8d7da; + color: #721c24; +} + +.progress-launched { + background-color: #d1ecf1; + color: #0c5460; +} + +/* Quick Action Cards for Dashboard */ +.quick-actions { + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 20px; + margin-bottom: 25px; +} + +@media (max-width: 1200px) { + .quick-actions { + grid-template-columns: repeat(2, 1fr); + } +} + +@media (max-width: 768px) { + .quick-actions { + grid-template-columns: 1fr; + } +} + +.quick-action-card { + background: linear-gradient(135deg, var(--white) 0%, #f8f9fa 100%); + border-radius: 12px; + padding: 20px; + display: flex; + align-items: center; + gap: 15px; + box-shadow: var(--shadow-sm); + cursor: pointer; + transition: all 0.3s ease; + border-left: 4px solid transparent; +} + +.quick-action-card:hover { + transform: translateY(-3px); + box-shadow: var(--shadow-md); +} + +.quick-action-card.alert { + border-left-color: #dc3545; +} + +.quick-action-card.warning { + border-left-color: #ffc107; +} + +.quick-action-card.info { + border-left-color: #17a2b8; +} + +.quick-action-card.success { + border-left-color: #28a745; +} + +.quick-action-icon { + width: 50px; + height: 50px; + border-radius: 12px; + display: flex; + align-items: center; + justify-content: center; + font-size: 1.3rem; +} + +.quick-action-card.alert .quick-action-icon { + background-color: rgba(220, 53, 69, 0.1); + color: #dc3545; +} + +.quick-action-card.warning .quick-action-icon { + background-color: rgba(255, 193, 7, 0.1); + color: #ffc107; +} + +.quick-action-card.info .quick-action-icon { + background-color: rgba(23, 162, 184, 0.1); + color: #17a2b8; +} + +.quick-action-card.success .quick-action-icon { + background-color: rgba(40, 167, 69, 0.1); + color: #28a745; +} + +.quick-action-content h4 { + margin: 0 0 5px 0; + font-size: 0.9rem; + color: var(--dark-gray); +} + +.quick-action-content .count { + font-size: 1.8rem; + font-weight: 700; + color: var(--dark-gray); +} + +.quick-action-content .label { + font-size: 0.8rem; + color: var(--medium-gray); +} + +/* Empty State Styles */ +.empty-state { + text-align: center; + padding: 60px 20px; + color: var(--medium-gray); +} + +.empty-state i { + font-size: 4rem; + color: var(--border-color); + margin-bottom: 20px; +} + +.empty-state h3 { + font-size: 1.2rem; + color: var(--dark-gray); + margin-bottom: 10px; +} + +.empty-state p { + font-size: 0.9rem; + margin-bottom: 20px; +} + +/* Trial Expiry Panel */ +.expiry-panel { + background-color: var(--white); + border-radius: 10px; + padding: 20px; + box-shadow: var(--shadow-sm); + margin-bottom: 20px; +} + +.expiry-panel-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 15px; + padding-bottom: 10px; + border-bottom: 1px solid var(--border-color); +} + +.expiry-panel-header h3 { + display: flex; + align-items: center; + gap: 10px; + font-size: 1rem; + color: var(--dark-gray); + margin: 0; +} + +.expiry-panel-header h3 i { + color: var(--primary-orange); +} + +.expiry-list { + max-height: 200px; + overflow-y: auto; +} + +.expiry-item { + display: flex; + align-items: center; + justify-content: space-between; + padding: 10px; + border-radius: 8px; + margin-bottom: 8px; + background-color: var(--light-gray); + transition: all 0.2s ease; +} + +.expiry-item:hover { + background-color: rgba(255, 107, 53, 0.1); +} + +.expiry-item-info { + display: flex; + align-items: center; + gap: 10px; +} + +.expiry-item-name { + font-weight: 600; + color: var(--dark-gray); +} + +.expiry-countdown { + font-size: 0.8rem; + padding: 3px 8px; + border-radius: 10px; +} + +.expiry-countdown.urgent { + background-color: #f8d7da; + color: #721c24; +} + +.expiry-countdown.warning { + background-color: #fff3cd; + color: #856404; +} + +.expiry-countdown.normal { + background-color: #cce5ff; + color: #004085; +} + +/* Smart Reminder Banner */ +.smart-reminder { + display: flex; + gap: 15px; + flex-wrap: wrap; + margin-bottom: 20px; +} + +.reminder-item { + display: flex; + align-items: center; + gap: 10px; + padding: 12px 18px; + border-radius: 8px; + font-size: 0.9rem; + font-weight: 500; + cursor: pointer; + transition: all 0.2s ease; +} + +.reminder-item:hover { + transform: translateY(-2px); +} + +.reminder-item.urgent { + background-color: #f8d7da; + color: #721c24; +} + +.reminder-item.warning { + background-color: #fff3cd; + color: #856404; +} + +.reminder-item i { + font-size: 1rem; +} + +/* Stat Card Trend Indicator */ +.stat-trend { + font-size: 0.8rem; + display: flex; + align-items: center; + gap: 4px; + margin-top: 5px; +} + +.stat-trend.up { + color: #28a745; +} + +.stat-trend.down { + color: #dc3545; +} + +.stat-trend.neutral { + color: var(--medium-gray); +} + +/* Table Row Enhancements */ +.trial-row { + transition: all 0.2s ease; +} + +.trial-row:hover { + background-color: rgba(255, 107, 53, 0.08) !important; +} + +.action-cell { + white-space: nowrap; + position: sticky; + right: 0; + background-color: var(--white); + box-shadow: -2px 0 5px rgba(0, 0, 0, 0.05); +} + +tr:hover .action-cell { + background-color: rgba(255, 107, 53, 0.08); +} + +/* Global Search Styles */ +.global-search-container { + position: relative; +} + +.search-box { + display: flex; + align-items: center; + background-color: var(--light-gray); + border-radius: 25px; + padding: 8px 15px; + width: 320px; + border: 2px solid transparent; + transition: all 0.3s ease; +} + +.search-box:focus-within { + border-color: var(--primary-orange); + background-color: var(--white); + box-shadow: 0 4px 15px rgba(255, 107, 53, 0.15); +} + +.search-shortcut { + background-color: rgba(0, 0, 0, 0.08); + padding: 2px 6px; + border-radius: 4px; + font-size: 0.7rem; + color: var(--medium-gray); + font-family: system-ui; + margin-left: auto; +} + +.search-results-dropdown { + position: absolute; + top: 100%; + left: 0; + right: 0; + background-color: var(--white); + border-radius: 12px; + box-shadow: 0 8px 30px rgba(0, 0, 0, 0.15); + margin-top: 8px; + max-height: 400px; + overflow-y: auto; + z-index: 2000; +} + +.search-section-title { + padding: 12px 15px 8px; + font-size: 0.75rem; + font-weight: 600; + color: var(--medium-gray); + text-transform: uppercase; + letter-spacing: 0.5px; + display: flex; + align-items: center; + gap: 8px; +} + +.search-section-title i { + font-size: 0.8rem; +} + +.search-result-item, +.search-history-item { + padding: 12px 15px; + cursor: pointer; + display: flex; + align-items: center; + gap: 12px; + transition: all 0.2s ease; + border-bottom: 1px solid rgba(0, 0, 0, 0.05); +} + +.search-result-item:hover, +.search-history-item:hover { + background-color: rgba(255, 107, 53, 0.08); +} + +.search-result-item i { + color: var(--primary-orange); + font-size: 1rem; +} + +.search-result-info { + flex: 1; +} + +.search-result-title { + font-weight: 600; + color: var(--dark-gray); + font-size: 0.9rem; +} + +.search-result-meta { + font-size: 0.75rem; + color: var(--medium-gray); + margin-top: 2px; +} + +.search-result-badge { + padding: 3px 8px; + border-radius: 12px; + font-size: 0.7rem; + font-weight: 600; +} + +.search-result-badge.trial { + background-color: #e3f2fd; + color: #1976d2; +} + +.search-result-badge.progress { + background-color: #fff3e0; + color: #f57c00; +} + +.search-result-badge.followup { + background-color: #e8f5e9; + color: #388e3c; +} + +.search-empty { + padding: 20px; + text-align: center; + color: var(--medium-gray); + font-size: 0.9rem; +} + +.search-history-item { + font-size: 0.85rem; + color: var(--dark-gray); +} + +.search-history-item i { + color: var(--medium-gray); +} + +/* Quick Add Dropdown Styles */ +.quick-add-dropdown-container { + position: relative; +} + +.quick-add-btn { + padding: 8px 16px; + font-size: 0.85rem; +} + +.quick-add-btn .fa-caret-down { + margin-left: 4px; + font-size: 0.8rem; +} + +.quick-add-dropdown { + position: absolute; + top: 100%; + right: 0; + background-color: var(--white); + border-radius: 10px; + box-shadow: 0 8px 30px rgba(0, 0, 0, 0.15); + margin-top: 8px; + min-width: 200px; + z-index: 2000; + overflow: hidden; +} + +.quick-add-menu-item { + padding: 12px 18px; + cursor: pointer; + display: flex; + align-items: center; + gap: 12px; + transition: all 0.2s ease; + color: var(--dark-gray); + font-size: 0.9rem; +} + +.quick-add-menu-item:hover { + background-color: rgba(255, 107, 53, 0.1); +} + +.quick-add-menu-item i { + width: 20px; + text-align: center; +} + +.quick-add-menu-item[data-action="trial"] i { + color: #17a2b8; +} + +.quick-add-menu-item[data-action="customer"] i { + color: #28a745; +} + +.quick-add-menu-item[data-action="followup"] i { + color: #ffc107; +} + +/* Color System Variables Update */ +:root { + --status-success: #27ae60; + --status-in-progress: #3498db; + --status-warning: #f39c12; + --status-error: #e74c3c; + --status-inactive: #95a5a6; + --brand-dark: #2c3e50; +} + +/* Responsive Improvements */ +@media (max-width: 1200px) { + .search-box { + width: 250px; + } + + .quick-add-btn span { + display: none; + } + + .quick-add-btn { + padding: 8px 12px; + } +} + +@media (max-width: 992px) { + .search-box { + width: 200px; + } + + .search-shortcut { + display: none; + } +} + +@media (max-width: 768px) { + .global-search-container { + display: none; + } + + .header-right { + gap: 10px; + } + + .menu-toggle { + display: block; + } + + .sidebar { + transform: translateX(-100%); + } + + .sidebar.show { + transform: translateX(0); + } + + .main-content { + margin-left: 0; + } +} + +/* Dashboard Cards Responsive */ +.dashboard-stats { + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 20px; +} + +@media (max-width: 1200px) { + .dashboard-stats { + grid-template-columns: repeat(2, 1fr); + } +} + +@media (max-width: 576px) { + .dashboard-stats { + grid-template-columns: 1fr; + } +} + +/* Enhanced Status Colors */ +.status-badge.status-active { + background-color: rgba(39, 174, 96, 0.15); + color: var(--status-success); + border-color: rgba(39, 174, 96, 0.3); +} + +.status-badge.status-warning { + background-color: rgba(243, 156, 18, 0.15); + color: var(--status-warning); + border-color: rgba(243, 156, 18, 0.3); +} + +.status-badge.status-expired, +.status-badge.status-urgent { + background-color: rgba(231, 76, 60, 0.15); + color: var(--status-error); + border-color: rgba(231, 76, 60, 0.3); +} + +.status-badge.status-inactive { + background-color: rgba(149, 165, 166, 0.15); + color: var(--status-inactive); + border-color: rgba(149, 165, 166, 0.3); +} + +.progress-badge.progress-completed { + background-color: rgba(39, 174, 96, 0.15); + color: var(--status-success); +} + +.progress-badge.progress-in-progress { + background-color: rgba(52, 152, 219, 0.15); + color: var(--status-in-progress); +} + +.progress-badge.progress-pending { + background-color: rgba(243, 156, 18, 0.15); + color: var(--status-warning); +} + +.progress-badge.progress-rejected { + background-color: rgba(231, 76, 60, 0.15); + color: var(--status-error); +} + +/* Keyboard Shortcut Indicator */ +.keyboard-shortcut { + background-color: rgba(0, 0, 0, 0.08); + padding: 2px 6px; + border-radius: 4px; + font-size: 0.7rem; + color: var(--medium-gray); + font-family: system-ui; } \ No newline at end of file diff --git a/frontend/index.html b/frontend/index.html index ec584b2..7b7972a 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -30,22 +30,37 @@
- + + - +
-
+
+ +
+
+ + +
+
+ + +
+
+ + +
+
+
+ @@ -259,7 +338,7 @@ -
+
@@ -288,15 +367,6 @@

已完成

-
-
- -
-
-

0

-

客户总数

-
-
@@ -381,6 +451,18 @@
+ +
+ + +
+
- +
@@ -710,11 +794,21 @@
'; + row.innerHTML = ` + + `; tbody.appendChild(row); return; } // Paginate data const startIndex = (trialCurrentPage - 1) * trialPageSize; - const endIndex = Math.min(startIndex + trialPageSize, trialPeriodsData.length); - const pageData = trialPeriodsData.slice(startIndex, endIndex); + const endIndex = Math.min(startIndex + trialPageSize, filteredTrialPeriodsData.length); + const pageData = filteredTrialPeriodsData.slice(startIndex, endIndex); pageData.forEach(period => { const row = document.createElement('tr'); + row.classList.add('trial-row'); // 显示客户名称,如果找不到则显示ID const customerName = customersMap[period.customerId] || period.customerId; + + // 计算状态和到期天数 + const now = new Date(); + const endDate = new Date(period.endTime); + const startDate = new Date(period.startTime); + const daysUntilExpiry = Math.ceil((endDate - now) / (1000 * 60 * 60 * 24)); + const isTrial = (period.isTrial !== undefined && period.isTrial !== null) ? period.isTrial : false; + + // 生成状态badge + let statusBadge = ''; + if (!isTrial) { + statusBadge = ' 非试用'; + } else if (daysUntilExpiry < 0) { + statusBadge = ' 已过期'; + } else if (daysUntilExpiry === 0) { + statusBadge = ' 今日到期'; + } else if (daysUntilExpiry <= 3) { + statusBadge = ` ${daysUntilExpiry}天后到期`; + } else if (daysUntilExpiry <= 7) { + statusBadge = ` ${daysUntilExpiry}天后到期`; + } else { + statusBadge = ' 试用中'; + } + const startTime = formatDateTime(period.startTime); const endTime = formatDateTime(period.endTime); const createdAt = formatDateTime(period.createdAt); row.innerHTML = ` - + + -
客户名称是否试用 开始时间 结束时间 创建时间 暂无试用时间记录 + +

👥 还没有客户试用信息

+

点击上方「添加试用时间」开始管理客户试用

+
${customerName}${customerName}${statusBadge} ${startTime} ${endTime} ${createdAt} + @@ -296,7 +366,7 @@ function updateTrialPagination() { pageBtn.textContent = i; pageBtn.addEventListener('click', () => { trialCurrentPage = i; - loadAllTrialPeriods(); + applyTrialFiltersAndSort(); }); pageNumbers.appendChild(pageBtn); } @@ -305,20 +375,16 @@ function updateTrialPagination() { // Open add trial modal function openAddTrialModal() { console.log('Opening add trial modal'); - - // Load customers first - loadCustomersForDropdown().then(() => { - console.log('Customers loaded, opening modal'); - document.getElementById('trialCustomerSelect').value = ''; - document.getElementById('trialStartTime').value = ''; - document.getElementById('trialEndTime').value = ''; - document.getElementById('addTrialPeriodModal').style.display = 'block'; - }); + document.getElementById('trialCustomerInput').value = ''; + document.querySelector('input[name="isTrial"][value="true"]').checked = true; + document.getElementById('trialStartTime').value = ''; + document.getElementById('trialEndTime').value = ''; + document.getElementById('addTrialPeriodModal').style.display = 'block'; } // Open edit trial modal function openEditTrialModal(periodId) { - const period = trialPeriodsData.find(p => p.id === periodId); + const period = filteredTrialPeriodsData.find(p => p.id === periodId); if (!period) return; document.getElementById('editTrialPeriodId').value = period.id; @@ -357,12 +423,14 @@ async function deleteTrialPeriodFromPage(periodId) { // Create trial period from page async function createTrialPeriodFromPage() { - const customerId = document.getElementById('trialCustomerSelect').value; + const customerName = document.getElementById('trialCustomerInput').value.trim(); + const isTrialValue = document.querySelector('input[name="isTrial"]:checked').value; + const isTrial = isTrialValue === 'true'; const startTime = document.getElementById('trialStartTime').value; const endTime = document.getElementById('trialEndTime').value; - if (!customerId) { - alert('请选择客户'); + if (!customerName) { + alert('请输入客户名称'); return; } @@ -371,10 +439,25 @@ async function createTrialPeriodFromPage() { return; } + // Find or create customer ID from customer name + let customerId = null; + for (const [id, name] of Object.entries(customersMap)) { + if (name === customerName) { + customerId = id; + break; + } + } + + // If customer doesn't exist in map, generate a new ID + if (!customerId) { + customerId = customerName; // Use customer name as ID for now + } + const formData = { customerId: customerId, startTime: new Date(startTime).toISOString(), - endTime: new Date(endTime).toISOString() + endTime: new Date(endTime).toISOString(), + isTrial: isTrial }; try { diff --git a/internal/handlers/followup_handler.go b/internal/handlers/followup_handler.go index c7e079a..acf1a8f 100644 --- a/internal/handlers/followup_handler.go +++ b/internal/handlers/followup_handler.go @@ -269,8 +269,6 @@ func (h *FollowUpHandler) GetCustomerList(w http.ResponseWriter, r *http.Request return } - fmt.Printf("DEBUG: Total customers from storage: %d\n", len(customers)) - // Create a list of customer objects with id and name type CustomerInfo struct { ID string `json:"id"` @@ -295,14 +293,10 @@ func (h *FollowUpHandler) GetCustomerList(w http.ResponseWriter, r *http.Request CustomerName: customer.CustomerName, }) seenNames[customer.CustomerName] = true - fmt.Printf("DEBUG: Added customer: ID=%s, Name=%s\n", customer.ID, customer.CustomerName) } } } - fmt.Printf("DEBUG: Total unique customer list items: %d\n", len(customerList)) - fmt.Printf("DEBUG: Total customer ID mappings: %d\n", len(customerMap)) - w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]interface{}{ "customers": customerList, // Deduplicated list for dropdown diff --git a/internal/handlers/trial_period_handler.go b/internal/handlers/trial_period_handler.go index e6e7db1..7eca434 100644 --- a/internal/handlers/trial_period_handler.go +++ b/internal/handlers/trial_period_handler.go @@ -86,6 +86,7 @@ func (h *TrialPeriodHandler) CreateTrialPeriod(w http.ResponseWriter, r *http.Re CustomerID: req.CustomerID, StartTime: startTime, EndTime: endTime, + IsTrial: req.IsTrial, CreatedAt: time.Now(), } @@ -311,8 +312,6 @@ func (h *TrialPeriodHandler) sendTrialNotification(customerID string, startTime, return } - log.Printf("DEBUG: Sending Feishu message from trial_period_handler: %s", string(jsonData)) - resp, err := http.Post(h.feishuWebhook, "application/json", bytes.NewBuffer(jsonData)) if err != nil { log.Printf("Failed to send Feishu notification: %v", err) @@ -327,3 +326,47 @@ func (h *TrialPeriodHandler) sendTrialNotification(customerID string, startTime, log.Printf("Sent trial notification for customer: %s (expires in %d days)", customerID, daysUntilExpiry) } + +// GetTrialCustomerList returns a list of unique customer names from trial periods +func (h *TrialPeriodHandler) GetTrialCustomerList(w http.ResponseWriter, r *http.Request) { + trialPeriods, err := h.storage.GetAllTrialPeriods() + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + // Create a set of unique customer IDs + customerIDs := make(map[string]bool) + for _, period := range trialPeriods { + if period.CustomerID != "" { + customerIDs[period.CustomerID] = true + } + } + + // Get unique customer names + seenNames := make(map[string]bool) + var customerNames []string + + for customerID := range customerIDs { + // Try to get customer name from customer storage + var customerName string + customer, err := h.customerStorage.GetCustomerByID(customerID) + if err == nil && customer != nil && customer.CustomerName != "" { + customerName = customer.CustomerName + } else { + // If customer not found, use customer ID as name + customerName = customerID + } + + // Add to list if not seen before + if !seenNames[customerName] { + customerNames = append(customerNames, customerName) + seenNames[customerName] = true + } + } + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(map[string]interface{}{ + "customerNames": customerNames, + }) +} diff --git a/models/trial_period.go b/models/trial_period.go index bbbf5a4..1f638fe 100644 --- a/models/trial_period.go +++ b/models/trial_period.go @@ -8,6 +8,7 @@ type TrialPeriod struct { CustomerID string `json:"customerId"` StartTime time.Time `json:"startTime"` EndTime time.Time `json:"endTime"` + IsTrial bool `json:"isTrial"` CreatedAt time.Time `json:"createdAt"` } @@ -16,6 +17,7 @@ type CreateTrialPeriodRequest struct { CustomerID string `json:"customerId"` StartTime string `json:"startTime"` EndTime string `json:"endTime"` + IsTrial bool `json:"isTrial"` } // UpdateTrialPeriodRequest represents the request to update a trial period