domain migration
This commit is contained in:
parent
192b810eb4
commit
c04a6ce6fe
26 changed files with 777 additions and 125 deletions
|
|
@ -3,6 +3,14 @@ import { useState } from "react";
|
|||
import { verifyCompanyDns } from "../api";
|
||||
import Brand from "./Brand";
|
||||
|
||||
const CHECK_LABELS = {
|
||||
ownership: "Domain ownership",
|
||||
mx: "MX",
|
||||
spf: "SPF",
|
||||
dkim: "DKIM",
|
||||
dmarc: "DMARC",
|
||||
};
|
||||
|
||||
function DnsRecordTable({ records }) {
|
||||
async function copyValue(value) {
|
||||
try {
|
||||
|
|
@ -99,8 +107,16 @@ export default function CompanyDnsSetup({ domain, account, onComplete, onLogout
|
|||
<DnsRecordTable records={domain.dns_records} />
|
||||
|
||||
<ul className="dns-notes">
|
||||
<li>
|
||||
Add the <strong>ownership</strong> TXT first — this proves you control the domain.
|
||||
Without it, verification cannot succeed even if MX/SPF look correct.
|
||||
</li>
|
||||
<li>Point MX to <strong>{domain.mail_host}</strong> — you do not need a separate mail A record.</li>
|
||||
<li>SPF uses <code>include:_spf.getall.help</code> — that record must exist on <strong>getall.help</strong> (Host <code>_spf</code>, TXT <code>v=spf1 ip4:<mail-server-ip> ~all</code>).</li>
|
||||
<li>
|
||||
SPF uses <code>include:_spf.gentic.email</code>. The platform operator maintains
|
||||
<code>_spf</code> on <strong>gentic.email</strong>; you only add SPF on your domain.
|
||||
</li>
|
||||
<li>DMARC must match the exact value in the table — an existing third-party DMARC record is not accepted.</li>
|
||||
<li>DNS changes can take a few minutes to propagate.</li>
|
||||
</ul>
|
||||
|
||||
|
|
@ -113,7 +129,7 @@ export default function CompanyDnsSetup({ domain, account, onComplete, onLogout
|
|||
>
|
||||
{check.verified ? <CheckCircle2 size={16} /> : <XCircle size={16} />}
|
||||
<div>
|
||||
<strong>{check.purpose.toUpperCase()}</strong>
|
||||
<strong>{CHECK_LABELS[check.purpose] || check.purpose.toUpperCase()}</strong>
|
||||
<span>{check.message}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
229
frontend/src/components/CompanyOverviewSetup.jsx
Normal file
229
frontend/src/components/CompanyOverviewSetup.jsx
Normal file
|
|
@ -0,0 +1,229 @@
|
|||
import { ArrowRight, Check } from "lucide-react";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
const STEPS = [
|
||||
{ id: 1, title: "Add domain", hint: "Use your company domain for email" },
|
||||
{ id: 2, title: "Verify domain", hint: "Prove domain ownership" },
|
||||
{ id: 3, title: "Configure email DNS", hint: "Set up MX, SPF, DKIM" },
|
||||
{ id: 4, title: "Company details", hint: "Tell us about your organization" },
|
||||
{ id: 5, title: "Add employees", hint: "Create and invite your team" },
|
||||
{ id: 6, title: "Finish setup", hint: "Start using AGMail" },
|
||||
];
|
||||
|
||||
const ORG_SIZES = [
|
||||
"1-10 employees",
|
||||
"11-50 employees",
|
||||
"51-200 employees",
|
||||
"201-500 employees",
|
||||
"500+ employees",
|
||||
];
|
||||
|
||||
const COUNTRIES = [
|
||||
"India",
|
||||
"United States",
|
||||
"United Kingdom",
|
||||
"Canada",
|
||||
"Australia",
|
||||
"Singapore",
|
||||
"Other",
|
||||
];
|
||||
|
||||
function storageKey(accountId, suffix) {
|
||||
return `agmail_org_${suffix}_${accountId}`;
|
||||
}
|
||||
|
||||
function loadStep(accountId) {
|
||||
const saved = Number(localStorage.getItem(storageKey(accountId, "step")));
|
||||
if (saved >= 4 && saved <= 6) return saved;
|
||||
return 4;
|
||||
}
|
||||
|
||||
function loadDetails(account, companyDomain) {
|
||||
try {
|
||||
const raw = localStorage.getItem(storageKey(account.id, "details"));
|
||||
if (raw) return JSON.parse(raw);
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
return {
|
||||
companyName: companyDomain?.domain_name?.split(".")[0] || "",
|
||||
orgSize: "51-200 employees",
|
||||
country: "India",
|
||||
contactName: `${account.first_name} ${account.last_name}`.trim(),
|
||||
workEmail: account.email,
|
||||
};
|
||||
}
|
||||
|
||||
export function isOrgSetupComplete(accountId) {
|
||||
return localStorage.getItem(storageKey(accountId, "complete")) === "1";
|
||||
}
|
||||
|
||||
export default function CompanyOverviewSetup({ account, companyDomain, onComplete }) {
|
||||
const [step, setStep] = useState(() => loadStep(account.id));
|
||||
const [details, setDetails] = useState(() => loadDetails(account, companyDomain));
|
||||
|
||||
useEffect(() => {
|
||||
localStorage.setItem(storageKey(account.id, "step"), String(step));
|
||||
}, [account.id, step]);
|
||||
|
||||
function saveDetails(event) {
|
||||
event.preventDefault();
|
||||
localStorage.setItem(storageKey(account.id, "details"), JSON.stringify(details));
|
||||
setStep(5);
|
||||
}
|
||||
|
||||
function finishSetup() {
|
||||
localStorage.setItem(storageKey(account.id, "complete"), "1");
|
||||
localStorage.removeItem(storageKey(account.id, "step"));
|
||||
onComplete?.();
|
||||
}
|
||||
|
||||
return (
|
||||
<section className="org-setup">
|
||||
<header className="org-setup-head">
|
||||
<h1>Set up your organization</h1>
|
||||
<p>Get your company email up and running in a few simple steps.</p>
|
||||
</header>
|
||||
|
||||
<div className="org-setup-layout">
|
||||
<ol className="org-setup-steps" aria-label="Setup progress">
|
||||
{STEPS.map((item) => {
|
||||
const complete = item.id < step || (item.id <= 3);
|
||||
const active = item.id === step;
|
||||
return (
|
||||
<li
|
||||
key={item.id}
|
||||
className={`org-setup-step${complete ? " complete" : ""}${active ? " active" : ""}`}
|
||||
>
|
||||
<span className="org-setup-step-mark" aria-hidden="true">
|
||||
{complete ? <Check size={14} strokeWidth={2.5} /> : item.id}
|
||||
</span>
|
||||
<span className="org-setup-step-copy">
|
||||
<strong>{item.title}</strong>
|
||||
<small>{item.hint}</small>
|
||||
</span>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ol>
|
||||
|
||||
<div className="org-setup-panel">
|
||||
{step === 4 && (
|
||||
<>
|
||||
<h2>Company details</h2>
|
||||
<p className="org-setup-section-label">Organization information</p>
|
||||
<form className="org-setup-form" onSubmit={saveDetails}>
|
||||
<label>
|
||||
Company name <span aria-hidden="true">*</span>
|
||||
<input
|
||||
required
|
||||
value={details.companyName}
|
||||
onChange={(event) => setDetails((current) => ({
|
||||
...current,
|
||||
companyName: event.target.value,
|
||||
}))}
|
||||
placeholder="Acme Pvt Ltd"
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
Organization size <span aria-hidden="true">*</span>
|
||||
<select
|
||||
required
|
||||
value={details.orgSize}
|
||||
onChange={(event) => setDetails((current) => ({
|
||||
...current,
|
||||
orgSize: event.target.value,
|
||||
}))}
|
||||
>
|
||||
{ORG_SIZES.map((size) => (
|
||||
<option key={size} value={size}>{size}</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
Country <span aria-hidden="true">*</span>
|
||||
<select
|
||||
required
|
||||
value={details.country}
|
||||
onChange={(event) => setDetails((current) => ({
|
||||
...current,
|
||||
country: event.target.value,
|
||||
}))}
|
||||
>
|
||||
{COUNTRIES.map((country) => (
|
||||
<option key={country} value={country}>{country}</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
Your name <span aria-hidden="true">*</span>
|
||||
<input
|
||||
required
|
||||
value={details.contactName}
|
||||
onChange={(event) => setDetails((current) => ({
|
||||
...current,
|
||||
contactName: event.target.value,
|
||||
}))}
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
Your work email <span aria-hidden="true">*</span>
|
||||
<input
|
||||
required
|
||||
type="email"
|
||||
value={details.workEmail}
|
||||
onChange={(event) => setDetails((current) => ({
|
||||
...current,
|
||||
workEmail: event.target.value,
|
||||
}))}
|
||||
/>
|
||||
</label>
|
||||
<div className="org-setup-actions">
|
||||
<button type="submit" className="button button-purple">
|
||||
Continue <ArrowRight size={16} />
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</>
|
||||
)}
|
||||
|
||||
{step === 5 && (
|
||||
<>
|
||||
<h2>Add employees</h2>
|
||||
<p className="org-setup-lead">
|
||||
Invite teammates to <strong>{companyDomain?.domain_name}</strong>. You can add people now or skip and do this later from People.
|
||||
</p>
|
||||
<div className="org-setup-placeholder">
|
||||
Employee invites will be available here soon.
|
||||
</div>
|
||||
<div className="org-setup-actions">
|
||||
<button type="button" className="button button-purple" onClick={() => setStep(6)}>
|
||||
Continue <ArrowRight size={16} />
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{step === 6 && (
|
||||
<>
|
||||
<h2>Finish setup</h2>
|
||||
<p className="org-setup-lead">
|
||||
Domain <strong>{companyDomain?.domain_name}</strong> is connected. You're ready to use AGMail.
|
||||
</p>
|
||||
<ul className="org-setup-summary">
|
||||
<li><Check size={14} /> Domain added and verified</li>
|
||||
<li><Check size={14} /> Email DNS configured</li>
|
||||
<li><Check size={14} /> Organization details saved</li>
|
||||
</ul>
|
||||
<div className="org-setup-actions">
|
||||
<button type="button" className="button button-purple" onClick={finishSetup}>
|
||||
Go to inbox <ArrowRight size={16} />
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,7 +1,12 @@
|
|||
import {
|
||||
ChevronDown,
|
||||
CircleDollarSign,
|
||||
CircleEllipsis,
|
||||
Clock3,
|
||||
Crown,
|
||||
FileEdit,
|
||||
Globe,
|
||||
Home,
|
||||
Inbox,
|
||||
ListChecks,
|
||||
LogOut,
|
||||
|
|
@ -10,16 +15,20 @@ import {
|
|||
Plug,
|
||||
Send,
|
||||
Settings,
|
||||
Shield,
|
||||
ShieldAlert,
|
||||
Sparkles,
|
||||
Star,
|
||||
Trash2,
|
||||
Users,
|
||||
UsersRound,
|
||||
X,
|
||||
} from "lucide-react";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { getCurrentAccount, getMyCompanyDomain } from "../api";
|
||||
import Brand from "./Brand";
|
||||
import CompanyDnsSetup from "./CompanyDnsSetup";
|
||||
import CompanyOverviewSetup, { isOrgSetupComplete } from "./CompanyOverviewSetup";
|
||||
import ComposeModal from "./ComposeModal";
|
||||
import MailboxView from "./MailboxView";
|
||||
|
||||
|
|
@ -49,6 +58,18 @@ const agentItems = [
|
|||
[Plug, "Integrations", "integrations"],
|
||||
];
|
||||
|
||||
const companyItems = [
|
||||
[Home, "Overview"],
|
||||
[Users, "People"],
|
||||
[UsersRound, "Groups"],
|
||||
[Globe, "Domains"],
|
||||
[CircleEllipsis, "Aliases"],
|
||||
[Shield, "Security"],
|
||||
[Crown, "Administrators"],
|
||||
[CircleDollarSign, "Billing"],
|
||||
[Settings, "Settings"],
|
||||
];
|
||||
|
||||
function clearSession() {
|
||||
sessionStorage.removeItem("agmail_access_token");
|
||||
sessionStorage.removeItem("agmail_account");
|
||||
|
|
@ -82,6 +103,7 @@ export default function DashboardPage() {
|
|||
const [lens, setLens] = useState("all");
|
||||
const [agentNav, setAgentNav] = useState(null);
|
||||
const [labelsExpanded, setLabelsExpanded] = useState(false);
|
||||
const [companyActive, setCompanyActive] = useState(null);
|
||||
const [mailStats, setMailStats] = useState({
|
||||
unread: 0,
|
||||
starred: 0,
|
||||
|
|
@ -103,7 +125,7 @@ export default function DashboardPage() {
|
|||
.then(async (currentAccount) => {
|
||||
sessionStorage.setItem("agmail_account", JSON.stringify(currentAccount));
|
||||
setAccount(currentAccount);
|
||||
if (currentAccount.account_type === "company") {
|
||||
if (currentAccount.account_type === "company" && currentAccount.is_domain_owner) {
|
||||
try {
|
||||
const domain = await getMyCompanyDomain(token);
|
||||
setCompanyDomain(domain);
|
||||
|
|
@ -120,12 +142,26 @@ export default function DashboardPage() {
|
|||
});
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!account?.is_domain_owner || needsDnsSetup) return;
|
||||
if (!isOrgSetupComplete(account.id)) {
|
||||
setCompanyActive("Overview");
|
||||
}
|
||||
}, [account, needsDnsSetup]);
|
||||
|
||||
function logout() {
|
||||
clearSession();
|
||||
window.location.assign("/login");
|
||||
}
|
||||
|
||||
function selectCompanyItem(label) {
|
||||
setCompanyActive(label);
|
||||
setAgentNav(null);
|
||||
setMenuOpen(false);
|
||||
}
|
||||
|
||||
function selectMailItem(item) {
|
||||
setCompanyActive(null);
|
||||
setAgentNav(null);
|
||||
if (item === "Starred") {
|
||||
setActive("Inbox");
|
||||
|
|
@ -141,6 +177,7 @@ export default function DashboardPage() {
|
|||
}
|
||||
|
||||
function selectLabel(name) {
|
||||
setCompanyActive(null);
|
||||
setAgentNav(null);
|
||||
setActive("Inbox");
|
||||
setLens(name);
|
||||
|
|
@ -148,6 +185,7 @@ export default function DashboardPage() {
|
|||
}
|
||||
|
||||
function selectAgentItem(item, actionKey) {
|
||||
setCompanyActive(null);
|
||||
setActive("Inbox");
|
||||
setLens("all");
|
||||
setAgentNav(actionKey);
|
||||
|
|
@ -190,6 +228,7 @@ export default function DashboardPage() {
|
|||
onComplete={() => {
|
||||
setCompanyDomain({ ...companyDomain, status: "active" });
|
||||
setNeedsDnsSetup(false);
|
||||
setCompanyActive("Overview");
|
||||
}}
|
||||
onLogout={logout}
|
||||
/>
|
||||
|
|
@ -200,6 +239,7 @@ export default function DashboardPage() {
|
|||
const workspaceTitle = account.account_type === "company" && companyDomain
|
||||
? companyDomain.domain_name
|
||||
: "Personal workspace";
|
||||
const isDomainOwner = account.account_type === "company" && account.is_domain_owner;
|
||||
const mailActive = lens === "starred"
|
||||
? "Starred"
|
||||
: active === "Outbox"
|
||||
|
|
@ -236,6 +276,24 @@ export default function DashboardPage() {
|
|||
</button>
|
||||
</div>
|
||||
|
||||
{isDomainOwner && (
|
||||
<SidebarSection title="Company" className="dashboard-sidebar-company dashboard-sidebar-first">
|
||||
<nav aria-label="Company">
|
||||
{companyItems.map(([ItemIcon, label]) => (
|
||||
<button
|
||||
className={`dashboard-nav-item dashboard-company-item${companyActive === label ? " active" : ""}`}
|
||||
key={label}
|
||||
type="button"
|
||||
onClick={() => selectCompanyItem(label)}
|
||||
>
|
||||
<ItemIcon size={16} strokeWidth={1.75} />
|
||||
<span>{label}</span>
|
||||
</button>
|
||||
))}
|
||||
</nav>
|
||||
</SidebarSection>
|
||||
)}
|
||||
|
||||
<SidebarSection title="Mail" className="dashboard-sidebar-mail">
|
||||
<nav aria-label="Mail">
|
||||
{mailItems.map(([ItemIcon, label, countKey]) => (
|
||||
|
|
@ -303,13 +361,19 @@ export default function DashboardPage() {
|
|||
</aside>
|
||||
|
||||
<main className="dashboard-content">
|
||||
{!["Inbox", "Outbox", "Trash"].includes(active) && (
|
||||
{companyActive && companyActive !== "Overview" && (
|
||||
<div className="dashboard-content-heading">
|
||||
<div><p>{workspaceTitle}</p><h1>{companyActive}</h1></div>
|
||||
<span className="dashboard-account-pill">{workspaceLabel}</span>
|
||||
</div>
|
||||
)}
|
||||
{!companyActive && !["Inbox", "Outbox", "Trash"].includes(active) && (
|
||||
<div className="dashboard-content-heading">
|
||||
<div><p>{workspaceTitle}</p><h1>{active}</h1></div>
|
||||
<span className="dashboard-account-pill">{workspaceLabel}</span>
|
||||
</div>
|
||||
)}
|
||||
{active === "Inbox" && (
|
||||
{!companyActive && active === "Inbox" && (
|
||||
<MailboxView
|
||||
mailbox="inbox"
|
||||
title="Inbox"
|
||||
|
|
@ -322,7 +386,7 @@ export default function DashboardPage() {
|
|||
onStats={handleStats}
|
||||
/>
|
||||
)}
|
||||
{active === "Outbox" && (
|
||||
{!companyActive && active === "Outbox" && (
|
||||
<MailboxView
|
||||
mailbox="sent"
|
||||
title="Sent"
|
||||
|
|
@ -332,7 +396,7 @@ export default function DashboardPage() {
|
|||
onOpenSettings={() => selectMailItem("Settings")}
|
||||
/>
|
||||
)}
|
||||
{active === "Trash" && (
|
||||
{!companyActive && active === "Trash" && (
|
||||
<MailboxView
|
||||
mailbox="trash"
|
||||
title="Trash"
|
||||
|
|
@ -342,7 +406,28 @@ export default function DashboardPage() {
|
|||
onOpenSettings={() => selectMailItem("Settings")}
|
||||
/>
|
||||
)}
|
||||
{!["Inbox", "Outbox", "Trash"].includes(active) && (
|
||||
{companyActive === "Overview" && isDomainOwner && (
|
||||
<CompanyOverviewSetup
|
||||
account={account}
|
||||
companyDomain={companyDomain}
|
||||
onComplete={() => {
|
||||
setCompanyActive(null);
|
||||
setActive("Inbox");
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{companyActive && companyActive !== "Overview" && (
|
||||
<section className="dashboard-empty dashboard-company-view">
|
||||
<span><Sparkles size={24} /></span>
|
||||
<h2>{companyActive}</h2>
|
||||
<p>
|
||||
{companyActive === "Domains" && companyDomain
|
||||
? `Manage DNS and delivery for ${companyDomain.domain_name}.`
|
||||
: `Company ${companyActive.toLowerCase()} controls will appear here.`}
|
||||
</p>
|
||||
</section>
|
||||
)}
|
||||
{!companyActive && !["Inbox", "Outbox", "Trash"].includes(active) && (
|
||||
<section className="dashboard-empty">
|
||||
<span><Sparkles size={24} /></span>
|
||||
<h2>{active} is ready for you</h2>
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ export default function LoginPage() {
|
|||
<>
|
||||
<div className="card-heading"><h2>Log in to agmail</h2><p>Enter your account details below.</p></div>
|
||||
<form className="auth-form" onSubmit={handleSubmit}>
|
||||
<label>Email address<div className="input-wrap"><Mail size={16} /><input name="email" type="email" placeholder="you@getall.help" required autoComplete="email" /></div></label>
|
||||
<label>Email address<div className="input-wrap"><Mail size={16} /><input name="email" type="email" placeholder="you@gentic.email" required autoComplete="email" /></div></label>
|
||||
<label>Password<div className="input-wrap"><LockKeyhole size={16} /><input name="password" type="password" placeholder="Your password" required autoComplete="current-password" /></div></label>
|
||||
{status.error && <p className="form-error" role="alert">{status.error}</p>}
|
||||
<button className="button button-coral auth-submit" disabled={status.loading}>
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ export default function SignupPage() {
|
|||
<label>Company domain<div className="input-wrap"><Building2 size={16} /><input name="company_domain" placeholder="yourcompany.com" required /></div></label>
|
||||
)}
|
||||
<label>Choose your email name
|
||||
<div className="email-builder input-wrap"><Mail size={16} /><input name="email_name" placeholder="alex" pattern="[A-Za-z0-9](?:[A-Za-z0-9._-]{0,62}[A-Za-z0-9])?" required /><span>{accountType === "individual" ? "@getall.help" : "@your domain"}</span></div>
|
||||
<div className="email-builder input-wrap"><Mail size={16} /><input name="email_name" placeholder="alex" pattern="[A-Za-z0-9](?:[A-Za-z0-9._-]{0,62}[A-Za-z0-9])?" required /><span>{accountType === "individual" ? "@gentic.email" : "@your domain"}</span></div>
|
||||
</label>
|
||||
<div className="field-row">
|
||||
<label>First name<div className="input-wrap"><UserRound size={16} /><input name="first_name" placeholder="Alex" required /></div></label>
|
||||
|
|
|
|||
|
|
@ -210,8 +210,8 @@ export default function SmtpCredentialsPage() {
|
|||
<input
|
||||
name="domain"
|
||||
type="text"
|
||||
placeholder="getall.help"
|
||||
defaultValue={result?.domain || "getall.help"}
|
||||
placeholder="gentic.email"
|
||||
defaultValue={result?.domain || "gentic.email"}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -176,6 +176,41 @@ h1 em { color:var(--coral); font-style:normal; font-weight:500; }
|
|||
.company-dns-check.ok { background:#eef8f1; color:#2f6b43; }
|
||||
.company-dns-check.fail { background:#fff0eb; color:#b04a38; }
|
||||
.company-dns-next { align-self:flex-start; margin-top:8px; display:inline-flex; align-items:center; gap:8px; }
|
||||
.button-purple { min-height:44px; padding:0 20px; display:inline-flex; align-items:center; gap:8px; border:0; border-radius:10px; background:#7357F6; color:#fff; font-size:13px; font-weight:600; cursor:pointer; box-shadow:0 8px 20px rgba(115,87,246,0.28); }
|
||||
.button-purple:hover { filter:brightness(1.03); }
|
||||
.org-setup { padding:8px 0 24px; }
|
||||
.org-setup-head { margin-bottom:28px; }
|
||||
.org-setup-head h1 { margin:0 0 8px; font:700 clamp(28px,3vw,36px)/1.15 "DM Sans"; letter-spacing:-.6px; color:#1a1628; }
|
||||
.org-setup-head p { margin:0; color:#77758A; font-size:14px; }
|
||||
.org-setup-layout { display:grid; grid-template-columns:minmax(220px,280px) minmax(0,1fr); gap:28px; align-items:start; }
|
||||
.org-setup-steps { margin:0; padding:0; list-style:none; display:grid; gap:0; }
|
||||
.org-setup-step { padding:12px 14px; display:grid; grid-template-columns:32px 1fr; gap:12px; align-items:start; border-radius:12px; color:#6f6c7b; }
|
||||
.org-setup-step-mark { width:32px; height:32px; display:grid; place-items:center; border-radius:50%; border:1px solid #E4E1E8; background:#fff; font-size:13px; font-weight:600; color:#6f6c7b; }
|
||||
.org-setup-step-copy { display:grid; gap:2px; }
|
||||
.org-setup-step-copy strong { font-size:13px; font-weight:600; color:#3d3948; }
|
||||
.org-setup-step-copy small { font-size:11px; color:#8d8792; line-height:1.35; }
|
||||
.org-setup-step.complete .org-setup-step-mark { border-color:#7357F6; background:#7357F6; color:#fff; }
|
||||
.org-setup-step.complete .org-setup-step-copy strong { color:#5a4fcf; }
|
||||
.org-setup-step.active { background:#F6F2FF; }
|
||||
.org-setup-step.active .org-setup-step-mark { border-color:#7357F6; background:#7357F6; color:#fff; }
|
||||
.org-setup-step.active .org-setup-step-copy strong { color:#5a4fcf; }
|
||||
.org-setup-panel { padding:28px 32px; border:1px solid #ECE9F2; border-radius:16px; background:#fff; box-shadow:0 8px 28px rgba(20,18,30,0.04); }
|
||||
.org-setup-panel h2 { margin:0 0 18px; font:700 22px/1.2 "DM Sans"; color:#1a1628; }
|
||||
.org-setup-section-label { margin:0 0 16px; font-size:12px; font-weight:600; color:#8d8792; }
|
||||
.org-setup-lead { margin:0 0 20px; color:#6f6c7b; font-size:14px; line-height:1.5; }
|
||||
.org-setup-form { display:grid; gap:14px; }
|
||||
.org-setup-form label { display:grid; gap:6px; font-size:12px; font-weight:600; color:#4a4652; }
|
||||
.org-setup-form label span { color:#e15b45; }
|
||||
.org-setup-form input,.org-setup-form select { height:42px; padding:0 12px; border:1px solid #E1DFE7; border-radius:10px; background:#fff; font-size:13px; color:#1a1628; }
|
||||
.org-setup-form input:focus,.org-setup-form select:focus { outline:none; border-color:#C8B8FF; box-shadow:0 0 0 3px rgba(115,87,246,0.12); }
|
||||
.org-setup-actions { margin-top:8px; display:flex; justify-content:flex-end; }
|
||||
.org-setup-placeholder { margin-bottom:20px; padding:24px; border:1px dashed #E4E1E8; border-radius:12px; background:#FAFAFC; color:#8d8792; font-size:13px; text-align:center; }
|
||||
.org-setup-summary { margin:0 0 24px; padding:0; list-style:none; display:grid; gap:10px; }
|
||||
.org-setup-summary li { display:flex; align-items:center; gap:8px; color:#3d3948; font-size:13px; font-weight:500; }
|
||||
.org-setup-summary svg { color:#7357F6; flex-shrink:0; }
|
||||
@media(max-width:900px) {
|
||||
.org-setup-layout { grid-template-columns:1fr; }
|
||||
}
|
||||
.dns-copy { flex-shrink:0; border:0; border-radius:7px; padding:4px; background:#f7f3f1; color:#8c8790; cursor:pointer; }
|
||||
.dns-copy:hover { background:#fff0eb; color:var(--coral); }
|
||||
.dashboard-scrim { display:none; }
|
||||
|
|
@ -471,7 +506,9 @@ h1 em { color:var(--coral); font-style:normal; font-weight:500; }
|
|||
.label-dot-green { background:#8ed9ad; }
|
||||
.dashboard-agent-item { color:#c3bed0; }
|
||||
.dashboard-agent-item.active { background:linear-gradient(90deg,#8b7cff22 0%,#ffffff10 100%); box-shadow:inset 3px 0 0 #9f8cff; }
|
||||
.dashboard-sidebar-mail,.dashboard-sidebar-labels,.dashboard-sidebar-agent { flex:0 0 auto; }
|
||||
.dashboard-sidebar-mail,.dashboard-sidebar-labels,.dashboard-sidebar-agent,.dashboard-sidebar-company { flex:0 0 auto; }
|
||||
.dashboard-sidebar-company.dashboard-sidebar-first { margin-top:var(--space-4); padding-top:0; border-top:0; }
|
||||
.dashboard-company-item.active { background:rgba(255,255,255,0.09); box-shadow:inset 2px 0 0 #a78bfa; }
|
||||
.dashboard-profile { margin-top:auto; padding-top:var(--space-3); border-top:1px solid #ffffff10; flex:0 0 auto; }
|
||||
.dashboard-profile>span { background:linear-gradient(180deg,#ff9a82,#ff6f57); color:#2a2038; }
|
||||
.dashboard-profile strong { color:#fff; }
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue