Compare commits
4
Commits
sync_readme_v3
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bef83d80a1 | ||
|
|
8ed1ff152b | ||
|
|
d2c8bbfcb2 | ||
|
|
da1be650ea |
@@ -8,8 +8,9 @@ dependencies = [
|
||||
"pgvector>=0.3.6",
|
||||
"psycopg[binary,pool]>=3.2",
|
||||
"pydantic>=2.0.0",
|
||||
"pydantic-ai>=1.67.0",
|
||||
"pydantic-ai-slim[voyageai]>=1.67.0",
|
||||
# <2 cap: 1.99.0 patches CVE-2026-46678; 2.0 is an untested major migration
|
||||
"pydantic-ai>=1.99.0,<2.0.0",
|
||||
"pydantic-ai-slim[voyageai]>=1.99.0,<2.0.0",
|
||||
"pydantic-settings>=2.0.0",
|
||||
"python-dotenv>=1.2.1",
|
||||
"sqlite-vec>=0.1.6",
|
||||
|
||||
Generated
+469
-332
File diff suppressed because it is too large
Load Diff
@@ -35,7 +35,11 @@ export function EnterpriseUpsell({ bare = false }: Props) {
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
<Button size="sm" onClick={() => setActiveView("procurement")}>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="secondary"
|
||||
onClick={() => setActiveView("procurement")}
|
||||
>
|
||||
{t("portal.billing.enterpriseUpsell.cta", "Explore Enterprise")}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -119,7 +119,7 @@
|
||||
/* Free grant still offsetting spend — reads as a positive credit note, pulled up
|
||||
under the "PDFs processed" line. */
|
||||
.portal-billing__free-remaining {
|
||||
color: var(--color-green-600, var(--color-green));
|
||||
color: var(--color-green);
|
||||
margin-top: -0.5rem;
|
||||
}
|
||||
|
||||
@@ -171,7 +171,7 @@
|
||||
}
|
||||
|
||||
.portal-billing__error {
|
||||
color: var(--color-red, #b91c1c);
|
||||
color: var(--color-red, var(--color-red-dark));
|
||||
font-size: 0.875rem;
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
@@ -228,11 +228,7 @@
|
||||
|
||||
.portal-billing__meter-fill {
|
||||
height: 100%;
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
var(--color-blue, #0a8bff),
|
||||
var(--color-purple, #8b5cf6)
|
||||
);
|
||||
background: linear-gradient(90deg, var(--color-blue), var(--color-purple));
|
||||
border-radius: 999px;
|
||||
transition: width 200ms ease;
|
||||
}
|
||||
@@ -308,28 +304,28 @@
|
||||
border-radius: 999px;
|
||||
}
|
||||
.payg-status[data-state="FULL"] {
|
||||
background: #dcfce7;
|
||||
color: #15803d;
|
||||
background: var(--color-green-light);
|
||||
color: var(--color-green-dark);
|
||||
}
|
||||
.payg-status[data-state="FULL"] .payg-status__dot {
|
||||
background: #22c55e;
|
||||
box-shadow: 0 0 0 3px rgba(34, 197, 94, 0.18);
|
||||
background: var(--color-green);
|
||||
box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-green) 18%, transparent);
|
||||
}
|
||||
.payg-status[data-state="WARNED"] {
|
||||
background: #fef3c7;
|
||||
color: #a16207;
|
||||
background: var(--color-amber-light);
|
||||
color: var(--color-amber-dark);
|
||||
}
|
||||
.payg-status[data-state="WARNED"] .payg-status__dot {
|
||||
background: #eab308;
|
||||
box-shadow: 0 0 0 3px rgba(234, 179, 8, 0.2);
|
||||
background: var(--color-amber);
|
||||
box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-amber) 20%, transparent);
|
||||
}
|
||||
.payg-status[data-state="DEGRADED"] {
|
||||
background: #fee2e2;
|
||||
color: #b91c1c;
|
||||
background: var(--color-red-light);
|
||||
color: var(--color-red-dark);
|
||||
}
|
||||
.payg-status[data-state="DEGRADED"] .payg-status__dot {
|
||||
background: #ef4444;
|
||||
box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.2);
|
||||
background: var(--color-red);
|
||||
box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-red) 20%, transparent);
|
||||
}
|
||||
|
||||
/* Segmented usage bar */
|
||||
@@ -347,13 +343,13 @@
|
||||
transition: width 0.5s cubic-bezier(0.16, 1, 0.3, 1);
|
||||
}
|
||||
.payg-bar__fill[data-state="FULL"] {
|
||||
background: linear-gradient(90deg, #0a8bff, #38bdf8);
|
||||
background: var(--color-blue);
|
||||
}
|
||||
.payg-bar__fill[data-state="WARNED"] {
|
||||
background: linear-gradient(90deg, #f59e0b, #fbbf24);
|
||||
background: var(--color-amber);
|
||||
}
|
||||
.payg-bar__fill[data-state="DEGRADED"] {
|
||||
background: linear-gradient(90deg, #dc2626, #f87171);
|
||||
background: var(--color-red);
|
||||
}
|
||||
|
||||
/* ── Plan card (free state) ─────────────────────────────────────────────── */
|
||||
@@ -394,7 +390,7 @@
|
||||
|
||||
.portal-billing__plan-features li::before {
|
||||
content: "✓";
|
||||
color: var(--color-green, #10b981);
|
||||
color: var(--color-green);
|
||||
position: absolute;
|
||||
left: 0;
|
||||
font-weight: 600;
|
||||
@@ -457,15 +453,15 @@
|
||||
}
|
||||
|
||||
.portal-billing__breakdown-fill--blue {
|
||||
background: #0a8bff;
|
||||
background: var(--color-blue);
|
||||
}
|
||||
|
||||
.portal-billing__breakdown-fill--purple {
|
||||
background: #8b5cf6;
|
||||
background: var(--color-purple);
|
||||
}
|
||||
|
||||
.portal-billing__breakdown-fill--teal {
|
||||
background: #06b6d4;
|
||||
background: var(--color-cat-extraction);
|
||||
}
|
||||
|
||||
/* ── Cap control ────────────────────────────────────────────────────────── */
|
||||
@@ -570,10 +566,10 @@
|
||||
Same class names + structure so the visual is identical to the SaaS Plan
|
||||
page; when the shared-component move lands, these dedupe to one stylesheet. */
|
||||
.scc {
|
||||
--scc-accent: #0a8bff;
|
||||
--scc-accent-text: #0a8bff;
|
||||
--scc-accent-soft: rgba(10, 139, 255, 0.12);
|
||||
--scc-accent-border: rgba(10, 139, 255, 0.25);
|
||||
--scc-accent: var(--color-blue);
|
||||
--scc-accent-text: var(--color-blue);
|
||||
--scc-accent-soft: color-mix(in srgb, var(--color-blue) 12%, transparent);
|
||||
--scc-accent-border: color-mix(in srgb, var(--color-blue) 25%, transparent);
|
||||
--scc-chip-bg: var(--color-bg-muted, #f8fafc);
|
||||
--scc-chip-border: var(--color-border, #e2e8f0);
|
||||
--scc-text-primary: var(--color-text-1, #0f172a);
|
||||
@@ -585,11 +581,11 @@
|
||||
gap: 14px;
|
||||
margin-top: 0.75rem;
|
||||
}
|
||||
[data-mantine-color-scheme="dark"] .scc {
|
||||
--scc-accent-text: #66b8ff;
|
||||
--scc-accent-soft: rgba(10, 139, 255, 0.16);
|
||||
--scc-chip-bg: #272d35;
|
||||
--scc-chip-border: #3d444e;
|
||||
[data-theme="dark"] .scc {
|
||||
--scc-accent-text: var(--color-blue);
|
||||
--scc-accent-soft: color-mix(in srgb, var(--color-blue) 16%, transparent);
|
||||
--scc-chip-bg: var(--color-bg-muted);
|
||||
--scc-chip-border: var(--color-border);
|
||||
}
|
||||
|
||||
.scc-row {
|
||||
@@ -736,9 +732,9 @@
|
||||
white-space: nowrap;
|
||||
}
|
||||
.portal-billing__role-pill[data-leader="true"] {
|
||||
background: rgba(10, 139, 255, 0.12);
|
||||
color: #0a8bff;
|
||||
border: 1px solid rgba(10, 139, 255, 0.25);
|
||||
background: color-mix(in srgb, var(--color-blue) 12%, transparent);
|
||||
color: var(--color-blue);
|
||||
border: 1px solid color-mix(in srgb, var(--color-blue) 25%, transparent);
|
||||
}
|
||||
.portal-billing__role-pill[data-leader="false"] {
|
||||
background: var(--color-bg-muted);
|
||||
@@ -768,10 +764,10 @@
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.portal-billing__planhead-lbl--free {
|
||||
color: #10b981;
|
||||
color: var(--color-green);
|
||||
}
|
||||
.portal-billing__planhead-lbl--meter {
|
||||
color: #0a8bff;
|
||||
color: var(--color-blue);
|
||||
}
|
||||
.portal-billing__planhead-title {
|
||||
margin: 0;
|
||||
@@ -839,7 +835,7 @@
|
||||
|
||||
.portal-billing__invoice-link:focus-visible {
|
||||
outline: none;
|
||||
border-color: var(--color-blue, #0a8bff);
|
||||
border-color: var(--color-blue);
|
||||
background: var(--color-bg-muted, #f1f5f9);
|
||||
}
|
||||
|
||||
@@ -953,6 +949,12 @@
|
||||
align-items: flex-start;
|
||||
gap: 1rem;
|
||||
}
|
||||
/* Keep the CTA on one line at its natural width — in the narrow subscribed
|
||||
column it was shrinking and clipping its label. */
|
||||
.portal-billing__enterprise-head > .sui-btn {
|
||||
flex-shrink: 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
/* Bare variant — embeds in another card's column without its own surface. */
|
||||
.portal-billing__enterprise-bare {
|
||||
display: block;
|
||||
@@ -988,13 +990,13 @@
|
||||
height: 100%;
|
||||
}
|
||||
.portal-billing__segbar-seg--blue {
|
||||
background: #0a8bff;
|
||||
background: var(--color-blue);
|
||||
}
|
||||
.portal-billing__segbar-seg--purple {
|
||||
background: #8b5cf6;
|
||||
background: var(--color-purple);
|
||||
}
|
||||
.portal-billing__segbar-seg--teal {
|
||||
background: #06b6d4;
|
||||
background: var(--color-cat-extraction);
|
||||
}
|
||||
.portal-billing__seglegend {
|
||||
display: flex;
|
||||
@@ -1016,13 +1018,13 @@
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.portal-billing__dot--blue {
|
||||
background: #0a8bff;
|
||||
background: var(--color-blue);
|
||||
}
|
||||
.portal-billing__dot--purple {
|
||||
background: #8b5cf6;
|
||||
background: var(--color-purple);
|
||||
}
|
||||
.portal-billing__dot--teal {
|
||||
background: #06b6d4;
|
||||
background: var(--color-cat-extraction);
|
||||
}
|
||||
.portal-billing__seglegend-label {
|
||||
font-weight: 600;
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Stroke-SVG icons for source types, replacing the black Unicode glyphs
|
||||
* (⛁ ☁ ✏ ◇) that rendered off-style and mis-centred inside the type tiles and
|
||||
* table badges. Keyed by the backend source `type`; unknown types fall back to
|
||||
* a neutral document mark.
|
||||
*/
|
||||
|
||||
const PATHS: Record<string, string> = {
|
||||
folder: "M3 7h6l2 2h10v9a1 1 0 01-1 1H4a1 1 0 01-1-1V7z",
|
||||
s3: "M7 18a4 4 0 010-8 5 5 0 019.6-1.3A3.5 3.5 0 0117 18H7z",
|
||||
editor: "M4 20h16M14 4l6 6-9 9H5v-6l9-9z",
|
||||
_default:
|
||||
"M14 3H7a1 1 0 00-1 1v16a1 1 0 001 1h10a1 1 0 001-1V7l-4-4zM14 3v4h4",
|
||||
};
|
||||
|
||||
export function SourceTypeIcon({ type }: { type: string }) {
|
||||
const d = PATHS[type] ?? PATHS._default;
|
||||
return (
|
||||
<svg
|
||||
className="portal-sources__type-svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
aria-hidden
|
||||
>
|
||||
<path
|
||||
d={d}
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.7"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
EDITOR_SOURCE_TYPE,
|
||||
sourceTypeMeta,
|
||||
} from "@portal/components/sources/sourceTypes";
|
||||
import { SourceTypeIcon } from "@portal/components/sources/SourceTypeIcon";
|
||||
import "@portal/views/Sources.css";
|
||||
|
||||
const STATUS_TONE: Record<SourceStatus, StatusTone> = {
|
||||
@@ -45,7 +46,7 @@ export function SourcesTable({ sources, onRowClick }: SourcesTableProps) {
|
||||
className={`portal-sources__type-dot portal-sources__type-dot--${meta.accent}`}
|
||||
aria-hidden
|
||||
>
|
||||
{meta.icon}
|
||||
<SourceTypeIcon type={s.type} />
|
||||
</span>
|
||||
<div className="portal-sources__name-text">
|
||||
<strong>{isEditor ? t(meta.labelKey) : s.name}</strong>
|
||||
|
||||
@@ -22,10 +22,12 @@ export function Pipelines() {
|
||||
const { isLoading } = useSectionFlags(state);
|
||||
|
||||
const pipelines = data?.pipelines ?? [];
|
||||
// Empty once the fetch settles with no pipelines (or fails → no data). Gates
|
||||
// both the KPI strip and the empty panel so no placeholder stat boxes sit
|
||||
// above an empty page.
|
||||
// Empty once the fetch settles with no pipelines (or fails → no data); gates
|
||||
// the empty panel below.
|
||||
const showEmpty = !isLoading && pipelines.length === 0;
|
||||
// The KPI strip is pure stat boxes: show it only once real pipelines exist, so
|
||||
// the loading and empty states don't flash a row of placeholder cards.
|
||||
const hasPipelines = pipelines.length > 0;
|
||||
|
||||
const openCreate = () =>
|
||||
navigate(`${toPortalPath(VIEW_PATHS.pipelines)}/new`);
|
||||
@@ -54,7 +56,7 @@ export function Pipelines() {
|
||||
</Button>
|
||||
</header>
|
||||
|
||||
{!showEmpty && <KpiStrip data={data} loading={loading} />}
|
||||
{hasPipelines && <KpiStrip data={data} loading={loading} />}
|
||||
|
||||
{isLoading && (
|
||||
<div className="portal-pipelines__table-skeleton" aria-hidden>
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
.portal-sources__name-text {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 0.25rem;
|
||||
min-width: 0;
|
||||
}
|
||||
@@ -366,7 +367,7 @@
|
||||
|
||||
.portal-sources__type-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
|
||||
gap: 0.625rem;
|
||||
}
|
||||
|
||||
@@ -376,6 +377,17 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* The shared Button lays its children out in a flex-row label; force the type
|
||||
tile back to a centred icon-over-label column so the SVG sits above the name
|
||||
with real spacing (not jammed against it). */
|
||||
.portal-sources__type-card .mantine-Button-label {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.portal-sources__type-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -402,9 +414,29 @@
|
||||
box-shadow: 0 0 0 1px var(--color-blue) inset;
|
||||
}
|
||||
|
||||
/* Icon inside the type picker: a plain stroke icon above the label (no badge
|
||||
box — the tinted box read as an extra bordered chip floating on the tile). */
|
||||
.portal-sources__type-icon {
|
||||
font-size: 1.25rem;
|
||||
color: var(--color-text-2);
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--color-text-3);
|
||||
}
|
||||
|
||||
.portal-sources__type-card.is-selected .portal-sources__type-icon {
|
||||
color: var(--color-blue);
|
||||
}
|
||||
|
||||
.portal-sources__type-icon .portal-sources__type-svg {
|
||||
width: 1.6rem;
|
||||
height: 1.6rem;
|
||||
}
|
||||
|
||||
/* Sized to sit centred inside the 1.875rem table badge / picker dot. */
|
||||
.portal-sources__type-svg {
|
||||
width: 1.05rem;
|
||||
height: 1.05rem;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.portal-sources__type-name {
|
||||
|
||||
Reference in New Issue
Block a user