Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import Features from './components/Features';
import UseCases from './components/UseCases';
import Pricing from './components/Pricing';
import CallToAction from './components/CallToAction';
import FAQ from './components/FAQ';
import Footer from './components/Footer';

function App() {
Expand All @@ -17,6 +18,7 @@ function App() {
<Features />
<UseCases />
<Pricing />
<FAQ />
<CallToAction />
</main>
<Footer />
Expand Down
77 changes: 77 additions & 0 deletions src/components/FAQ.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { useState } from 'react';
import FAQ_DATA from '../data/faq';

function FAQ() {
const [openId, setOpenId] = useState(null);

const toggle = (id) => {
setOpenId((prev) => (prev === id ? null : id));
};

return (
<section className="faq" id="faq" aria-labelledby="faq-heading">
<div className="container">
<div className="section-header">
<span className="section-header__badge">FAQ</span>
<h2 className="section-header__title" id="faq-heading">
常見問題
</h2>
<p className="section-header__desc">
快速了解 SalesPilot CRM 的功能與服務細節
</p>
</div>

<dl className="faq__list" role="presentation">
{FAQ_DATA.map((item) => {
const isOpen = openId === item.id;
return (
<div
key={item.id}
className={`faq__item ${isOpen ? 'faq__item--open' : ''}`}
>
<dt>
<button
className="faq__question"
onClick={() => toggle(item.id)}
aria-expanded={isOpen}
aria-controls={`${item.id}-answer`}
>
<span className="faq__question-text">{item.question}</span>
<svg
className={`faq__icon ${isOpen ? 'faq__icon--open' : ''}`}
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
aria-hidden="true"
>
<line x1="12" y1="5" x2="12" y2="19" />
<line x1="5" y1="12" x2="19" y2="12" />
</svg>
</button>
</dt>
<dd
id={`${item.id}-answer`}
className={`faq__answer ${isOpen ? 'faq__answer--open' : ''}`}
role="region"
aria-labelledby={`${item.id}-question`}
hidden={!isOpen}
>
<div className="faq__answer-inner">
<p className="faq__answer-text">{item.answer}</p>
</div>
</dd>
</div>
);
})}
</dl>
</div>
</section>
);
}

export default FAQ;
44 changes: 44 additions & 0 deletions src/data/faq.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
const FAQ_DATA = [
{
id: 'faq-1',
question: 'SalesPilot CRM 有免費試用期嗎?',
answer: '有的!我們提供 14 天全功能免費試用,無需信用卡即可註冊。試用期間你可以完整體驗所有功能,包含銷售管線管理、客戶追蹤、報表分析等。試用結束後,你可以選擇適合的付費方案繼續使用。',
},
{
id: 'faq-2',
question: '可以從其他 CRM 系統匯入資料嗎?',
answer: '可以。SalesPilot CRM 支援從多種常見 CRM 平台(如 Salesforce、HubSpot、Pipedrive)匯入客戶與交易資料。我們提供 CSV 批次匯入工具以及 API 介面,協助你快速完成資料遷移。',
},
{
id: 'faq-3',
question: '團隊成員數量有上限嗎?',
answer: '視方案而定。入門方案最多支援 5 位團隊成員,專業方案最多 20 位,企業方案則無限制。所有方案都支援權限分級管理,你可以依照角色設定每位成員的存取範圍。',
},
{
id: 'faq-4',
question: '資料安全如何保障?',
answer: '我們採用 AES-256 加密儲存所有靜態資料,傳輸過程使用 TLS 1.3 加密。伺服器部署於 SOC 2 Type II 認證的雲端機房,每日自動備份。我們也提供 SSO 單一登入與雙因素驗證(2FA)等進階安全功能。',
},
{
id: 'faq-5',
question: '有提供 API 或第三方整合嗎?',
answer: '是的,SalesPilot CRM 提供完整的 RESTful API 與 Webhook 機制,可與 Slack、Google Workspace、Microsoft 365、Zapier 等常用工具無縫整合。企業方案另提供專屬 API 金鑰與進階整合支援。',
},
{
id: 'faq-6',
question: '客戶支援的服務範圍是什麼?',
answer: '所有方案均提供線上客服與知識庫。專業方案以上享有優先客服通道(2 小時內回覆),企業方案另配置專屬客戶成功經理(CSM)與 24/7 緊急支援熱線。',
},
{
id: 'faq-7',
question: '可以在手機上使用嗎?',
answer: '可以!SalesPilot CRM 的網頁介面完全支援響應式設計(RWD),在手機與平板上皆可順暢操作。我們也提供 iOS 與 Android 原生 App,支援離線模式與推播通知,讓你在外也能即時追蹤銷售進度。',
},
{
id: 'faq-8',
question: '如何升級或降級方案?',
answer: '你可以隨時在後台的「帳戶設定」中變更方案。升級立即生效,差額按剩餘天數比例計算;降級則於下個計費週期生效,當期仍維持原方案權益。如有客製需求,歡迎聯繫我們的銷售團隊。',
},
];

export default FAQ_DATA;
97 changes: 97 additions & 0 deletions src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -1069,6 +1069,103 @@ button {
}
}

/* ========================================
FAQ
======================================== */
.faq {
padding: var(--space-24) 0;
background: var(--color-bg-elevated);
}

.faq__list {
max-width: 800px;
margin: 0 auto;
display: flex;
flex-direction: column;
gap: var(--space-3);
}

.faq__item {
background: var(--color-bg-card);
border: 1px solid var(--color-border);
border-radius: var(--radius-lg);
transition: all var(--transition-base);
overflow: hidden;
}

.faq__item:hover {
border-color: var(--color-border-hover);
}

.faq__item--open {
border-color: var(--color-primary);
box-shadow: 0 0 0 3px var(--color-primary-glow);
}

.faq__question {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
padding: var(--space-5) var(--space-6);
text-align: left;
font-size: var(--text-base);
font-weight: 600;
color: var(--color-text);
gap: var(--space-4);
transition: color var(--transition-fast);
}

.faq__question:hover {
color: var(--color-primary-light);
}

.faq__question-text {
flex: 1;
}

.faq__icon {
flex-shrink: 0;
color: var(--color-text-muted);
transition: transform var(--transition-base);
}

.faq__icon--open {
transform: rotate(45deg);
}

.faq__answer {
display: grid;
grid-template-rows: 0fr;
transition: grid-template-rows var(--transition-base);
}

.faq__answer--open {
grid-template-rows: 1fr;
}

.faq__answer-inner {
overflow: hidden;
}

.faq__answer-text {
padding: 0 var(--space-6) var(--space-5);
color: var(--color-text-secondary);
line-height: 1.7;
font-size: var(--text-sm);
}

@media (max-width: 480px) {
.faq__question {
padding: var(--space-4);
font-size: var(--text-sm);
}

.faq__answer-text {
padding: 0 var(--space-4) var(--space-4);
}
}

/* ========================================
CTA BANNER
======================================== */
Expand Down