From c6505a5037dff0d013d37e99dfd474a77414f5b9 Mon Sep 17 00:00:00 2001 From: Kevin Brown Date: Mon, 4 May 2026 21:32:59 +0300 Subject: [PATCH] style fixes --- .env.example | 5 +- .env.test | 3 + README.md | 2 +- core/settings_types.py | 2 + .../newsletter-maker/templates/configmap.yaml | 2 +- .../newsletter-maker/values-minikube.yaml | 2 +- .../helm/newsletter-maker/values-staging.yaml | 2 +- deploy/helm/newsletter-maker/values.yaml | 2 +- docker-compose.yml | 2 +- docs/admin-guide/configuration.md | 22 ++- docs/developer-guide/deployment.md | 40 ++++ docs/developer-guide/local-development.md | 19 +- docs/reference/tunables.md | 24 ++- frontend/.env.example | 2 +- frontend/next-env.d.ts | 2 +- frontend/package.json | 2 +- .../(home)/_components/ContentFeed/index.tsx | 15 +- .../DashboardFilterToolbar/index.test.tsx | 9 +- .../DashboardFilterToolbar/index.tsx | 28 +-- .../_components/DashboardOverview/index.tsx | 2 +- .../_components/DashboardSidebar/index.tsx | 6 +- .../HomePageContent/index.test.tsx | 2 +- .../_components/ReviewQueueTable/index.tsx | 12 +- .../SourceDiversityPanel/index.test.tsx | 5 +- .../SourceDiversityPanel/index.tsx | 22 +-- .../SourceHealthPanel/index.test.tsx | 5 +- .../_components/SourceHealthPanel/index.tsx | 14 +- .../TopicCentroidPanel/index.test.tsx | 5 +- .../_components/TopicCentroidPanel/index.tsx | 16 +- .../_components/TrendTaskRunsPanel/index.tsx | 6 +- .../app/admin/health/_components/helpers.ts | 119 ++++++++++++ frontend/src/app/admin/health/page.test.tsx | 36 ++-- frontend/src/app/admin/health/page.tsx | 182 +----------------- .../NewsletterIntakePanel/index.tsx | 6 +- frontend/src/app/admin/sources/page.test.tsx | 11 +- frontend/src/app/admin/sources/page.tsx | 15 +- .../ContentDetailMainColumn/index.test.tsx | 2 +- .../ContentDetailMainColumn/index.tsx | 39 ++-- .../ContentDetailSidebar/index.test.tsx | 19 +- .../ContentDetailSidebar/index.tsx | 60 +++--- .../[id]/_components/SkillActionBar/index.tsx | 2 - .../app/content/[id]/_components/helpers.ts | 14 ++ frontend/src/app/content/[id]/page.test.tsx | 15 +- frontend/src/app/content/[id]/page.tsx | 24 +-- .../DraftOverviewCards/index.test.tsx | 6 +- .../_components/DraftOverviewCards/index.tsx | 12 +- .../_components/DraftsList/index.test.tsx | 5 +- .../drafts/_components/DraftsList/index.tsx | 10 +- .../DraftsOverviewCards/index.test.tsx | 4 +- .../_components/DraftsOverviewCards/index.tsx | 10 +- .../EntityMentionsPanel/index.test.tsx | 5 +- .../_components/EntityMentionsPanel/index.tsx | 6 +- .../EntityOverviewCard/index.test.tsx | 1 + .../_components/EntityOverviewCard/index.tsx | 8 +- .../EntityCandidatesCard/index.test.tsx | 2 +- .../EntityCandidatesCard/index.tsx | 5 +- .../CandidateClusterCard/index.tsx | 9 +- .../ResolvedCandidateList/index.test.tsx | 2 +- .../ResolvedCandidateList/index.tsx | 8 +- .../src/app/entities/candidates/page.test.tsx | 4 +- frontend/src/app/entities/page.test.tsx | 2 +- frontend/src/app/globals.css | 164 +++++++++------- .../OriginalContentIdeaCard/index.test.tsx | 9 +- .../OriginalContentIdeaCard/index.tsx | 18 +- .../InvitationDetailsCard/index.tsx | 5 +- .../LoginPageContent/index.test.tsx | 3 + .../_components/LoginPageContent/index.tsx | 4 +- .../MessagesPageContent/index.test.tsx | 47 +++++ .../_components/MessagesPageContent/index.tsx | 8 +- frontend/src/app/shadcn.css | 94 +++++++++ .../ThemeSuggestionCard/index.test.tsx | 7 +- .../_components/ThemeSuggestionCard/index.tsx | 20 +- .../ThemesQueueOverview/index.test.tsx | 3 + .../_components/ThemesQueueOverview/index.tsx | 2 +- .../TopicClusterCard/index.test.tsx | 6 + .../_components/TopicClusterCard/index.tsx | 4 +- .../TrendClusterDetailPanel/index.tsx | 13 +- .../TrendsQueueOverview/index.test.tsx | 3 + .../_components/TrendsQueueOverview/index.tsx | 2 +- frontend/src/app/trends/page.tsx | 3 - .../_components/AppShellHeader/index.test.tsx | 22 ++- .../_components/AppShellHeader/index.tsx | 19 +- .../AppShellSidebar/index.test.tsx | 7 + .../_components/AppShellSidebar/index.tsx | 20 +- .../layout/AppShell/index.stories.tsx | 4 +- .../components/layout/AppShell/index.test.tsx | 12 +- .../src/components/layout/AppShell/index.tsx | 9 + frontend/src/components/ui/button.tsx | 3 +- frontend/src/lib/__tests__/api.test.ts | 2 +- frontend/src/lib/__tests__/auth.test.ts | 2 +- frontend/src/lib/api.ts | 4 +- frontend/src/lib/auth.ts | 6 +- frontend/src/lib/view-helpers.ts | 44 +++++ frontend/tsconfig.tsbuildinfo | 2 +- newsletter_maker/settings/__init__.py | 4 + newsletter_maker/settings/base.py | 15 +- newsletters/intake.py | 2 +- newsletters/tests/test_newsletters.py | 6 +- projects/linkedin_oauth.py | 2 +- projects/tests/test_api.py | 5 + trends/tests/test_tasks.py | 2 + 101 files changed, 956 insertions(+), 574 deletions(-) create mode 100644 frontend/src/app/admin/health/_components/helpers.ts create mode 100644 frontend/src/app/content/[id]/_components/helpers.ts create mode 100644 frontend/src/app/messages/_components/MessagesPageContent/index.test.tsx create mode 100644 frontend/src/app/shadcn.css diff --git a/.env.example b/.env.example index bf67ca9d..ebcfc9c4 100644 --- a/.env.example +++ b/.env.example @@ -76,12 +76,13 @@ DJANGO_SUPERUSER_USERNAME=admin DJANGO_SUPERUSER_EMAIL=admin@example.com DJANGO_SUPERUSER_PASSWORD=adminpass -NEWSLETTER_API_BASE_URL=http://nginx +NEWSLETTER_API_INTERNAL_URL=http://127.0.0.1:8080 +NEWSLETTER_PUBLIC_URL=http://127.0.0.1:8080 NEWSLETTER_API_USERNAME=admin NEWSLETTER_API_PASSWORD=adminpass DEBUG=True -ALLOWED_HOSTS=localhost,127.0.0.1,newslettermaker.tech +ALLOWED_HOSTS=localhost,127.0.0.1,nginx,newslettermaker.tech FRONTEND_URL=http://localhost:3000 diff --git a/.env.test b/.env.test index 37ac67cf..bf051611 100644 --- a/.env.test +++ b/.env.test @@ -8,3 +8,6 @@ REDDIT_CLIENT_SECRET=secret REDDIT_USER_AGENT=newsletter-maker/test CELERY_BROKER_URL=memory:// CELERY_RESULT_BACKEND=cache+memory:// +ALLOWED_HOSTS=localhost,127.0.0.1,nginx,testserver +NEWSLETTER_API_INTERNAL_URL=http://127.0.0.1:8080 +NEWSLETTER_PUBLIC_URL=http://127.0.0.1:8080 diff --git a/README.md b/README.md index b46eff96..5b3b1e8f 100644 --- a/README.md +++ b/README.md @@ -85,7 +85,7 @@ kubectl port-forward svc/newsletter-maker-newsletter-maker-nginx 8080:80 > Frontend credentials (from seed): > > Username: demo_editor -> Password: demo_password +> Password: demo-password **Command Summary:** diff --git a/core/settings_types.py b/core/settings_types.py index 9f64fc7a..bc2db27a 100644 --- a/core/settings_types.py +++ b/core/settings_types.py @@ -11,6 +11,8 @@ class CoreSettings(Protocol): LINKEDIN_OAUTH_SCOPES: str METRICS_TOKEN: str NEWSLETTER_API_BASE_URL: str + NEWSLETTER_API_INTERNAL_URL: str + NEWSLETTER_PUBLIC_URL: str QDRANT_URL: str EMBEDDING_MODEL: str EMBEDDING_PROVIDER: str diff --git a/deploy/helm/newsletter-maker/templates/configmap.yaml b/deploy/helm/newsletter-maker/templates/configmap.yaml index 8530dae0..e81b548d 100644 --- a/deploy/helm/newsletter-maker/templates/configmap.yaml +++ b/deploy/helm/newsletter-maker/templates/configmap.yaml @@ -13,7 +13,7 @@ data: CHANNEL_LAYER_URL: {{ default (include "newsletter-maker.redisUrl" .) .Values.env.channelLayerUrl | quote }} QDRANT_URL: {{ include "newsletter-maker.qdrantUrl" . | quote }} MESSAGING_ENABLED: {{ .Values.env.messagingEnabled | quote }} - NEWSLETTER_API_BASE_URL: {{ .Values.env.newsletterApiBaseUrl | quote }} + NEWSLETTER_PUBLIC_URL: {{ .Values.env.newsletterPublicUrl | quote }} EMAIL_BACKEND: {{ .Values.env.emailBackend | quote }} DEFAULT_FROM_EMAIL: {{ .Values.env.defaultFromEmail | quote }} SERVER_EMAIL: {{ .Values.env.serverEmail | quote }} diff --git a/deploy/helm/newsletter-maker/values-minikube.yaml b/deploy/helm/newsletter-maker/values-minikube.yaml index cf1883a4..84fbe217 100644 --- a/deploy/helm/newsletter-maker/values-minikube.yaml +++ b/deploy/helm/newsletter-maker/values-minikube.yaml @@ -7,7 +7,7 @@ env: debug: "true" allowedHosts: "localhost,127.0.0.1,newsletter-maker.local" csrfTrustedOrigins: "http://localhost,http://127.0.0.1,http://newsletter-maker.local" - newsletterApiBaseUrl: "http://newsletter-maker.local" + newsletterPublicUrl: "http://newsletter-maker.local" nginx: service: diff --git a/deploy/helm/newsletter-maker/values-staging.yaml b/deploy/helm/newsletter-maker/values-staging.yaml index 243ec651..42e4124d 100644 --- a/deploy/helm/newsletter-maker/values-staging.yaml +++ b/deploy/helm/newsletter-maker/values-staging.yaml @@ -8,7 +8,7 @@ env: allowedHosts: "staging.newsletter-maker.example.com" csrfTrustedOrigins: "https://staging.newsletter-maker.example.com" messagingEnabled: "true" - newsletterApiBaseUrl: "https://staging.newsletter-maker.example.com" + newsletterPublicUrl: "https://staging.newsletter-maker.example.com" logLevel: INFO secrets: diff --git a/deploy/helm/newsletter-maker/values.yaml b/deploy/helm/newsletter-maker/values.yaml index af66ee95..0ca1650b 100644 --- a/deploy/helm/newsletter-maker/values.yaml +++ b/deploy/helm/newsletter-maker/values.yaml @@ -13,7 +13,7 @@ env: siteId: "1" channelLayerUrl: "" messagingEnabled: "true" - newsletterApiBaseUrl: "http://newsletter-maker.local" + newsletterPublicUrl: "http://newsletter-maker.local" emailBackend: anymail.backends.resend.EmailBackend defaultFromEmail: onboarding@resend.dev serverEmail: onboarding@resend.dev diff --git a/docker-compose.yml b/docker-compose.yml index f81a5f62..60323d0a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -139,7 +139,7 @@ services: env_file: - .env environment: - NEWSLETTER_API_BASE_URL: http://nginx + NEWSLETTER_API_INTERNAL_URL: http://nginx NEXT_TELEMETRY_DISABLED: "1" depends_on: nginx: diff --git a/docs/admin-guide/configuration.md b/docs/admin-guide/configuration.md index b2b6fd7b..a32d31c6 100644 --- a/docs/admin-guide/configuration.md +++ b/docs/admin-guide/configuration.md @@ -3,29 +3,45 @@ See the [Tunables Reference](../reference/tunables.md) for the exact list of algorithms and thresholds. ## Required vs Optional Variables -**Required**: -* `DATABASE_URL`, `REDIS_URL`, `QDRANT_URL`, `SECRET_KEY`, `NEWSLETTER_API_BASE_URL`. + +**Required**: + +* `DATABASE_URL`, `REDIS_URL`, `QDRANT_URL`, `SECRET_KEY`, `NEWSLETTER_PUBLIC_URL`. + **Optional but critical for AI**: + * `OPENROUTER_API_KEY` (Required for relevance tie-breaking and categorization). ## Secrets Handling + * In Docker Compose: Loaded tightly from the `.env` file mapped securely to the container. * In Kubernetes: Expected to be mapped into the Pod `env` spec via Secrets. ## Internal vs Public URLs + Due to container networking: -* `NEWSLETTER_API_BASE_URL` (Internal) will reference inner hostnames like `http://nginx`. + +* `NEWSLETTER_API_INTERNAL_URL` (Internal) should reference inner hostnames like `http://nginx` when the frontend talks to the backend over a private Docker or Kubernetes network. * `NEWSLETTER_PUBLIC_URL` (Public) should point to your real FQDN (e.g. `https://news.mydomain.com`) used in emails. +For local Docker Compose development, the default split is usually: + +* `NEWSLETTER_API_INTERNAL_URL=http://nginx` +* `NEWSLETTER_PUBLIC_URL=http://127.0.0.1:8080` + ## Email Provider (Anymail) + Newsletter intake relies on Resend webhooks and Django Anymail forwarding. Configured via: + * `RESEND_API_KEY` * `RESEND_INBOUND_SECRET` * `DEFAULT_FROM_EMAIL` ## LLM Provider Routing + Select between `local`, `ollama` or remote providers using `EMBEDDING_PROVIDER`. Set URLs correctly to point to either the internal container (`http://ollama:11434`) or external APIs (`https://api.openai.com/v1`). ## OAuth Provider Toggles + If `LINKEDIN_CLIENT_ID` or `REDDIT_CLIENT_ID` are present, their respective capabilities light up dynamically in the application. diff --git a/docs/developer-guide/deployment.md b/docs/developer-guide/deployment.md index 641dc000..83b64397 100644 --- a/docs/developer-guide/deployment.md +++ b/docs/developer-guide/deployment.md @@ -1,19 +1,59 @@ # Deployment ## just build Contract + The `just build` target makes zero assumptions about the environment file. It uses `DOCKER_BUILDKIT=0` to ensure legacy build isolation and host image cache utilization. No `.env` copies are made during build time. ## Docker Compose + Used primarily for local testing and running the application on a single VPS. See [Admin Installation](../admin-guide/installation.md) for details. ## Helm Chart Layout + For Kubernetes deployments, a reusable Helm chart sits in `deploy/helm/`. +## Minikube Quick Start + +Use this path when you want to run the stack on a local Kubernetes cluster instead of Docker Compose. + +Prerequisites: + +- `minikube` +- `kubectl` +- `helm` + +Start Minikube and deploy the chart: + +```bash +minikube start +just k8s-build-minikube +just helm-lint +just helm-template +just k8s-install-minikube +``` + +Forward the Nginx service locally: + +```bash +kubectl port-forward svc/newsletter-maker-newsletter-maker-nginx 8080:80 +``` + +Then open in your browser. + +To remove the local release: + +```bash +just k8s-uninstall-minikube +``` + ## ArgoCD Application + We maintain an ArgoCD application manifest in `deploy/argocd/` to support GitOps continuous delivery. ## Staging Overlay + Staging branches utilize encrypted / sealed secrets (or external secret operators) pushed into the cluster. ## Prometheus ServiceMonitor + If deployed alongside the `kube-prometheus-stack`, the chart deploys a `ServiceMonitor` to scrape port 8000 for Django metrics exposed by `django-prometheus`. diff --git a/docs/developer-guide/local-development.md b/docs/developer-guide/local-development.md index 4af7dd8d..9c115f39 100644 --- a/docs/developer-guide/local-development.md +++ b/docs/developer-guide/local-development.md @@ -3,7 +3,7 @@ Newsletter Maker uses a **two-workflow split** to isolate fast local iteration from full full-stack fidelity. ## The Two-Workflow Split -1. **Host-Side Track**: Used for fast linting, typechecking, and unit tests WITHOUT spinning up Docker. +1. **Host-Side Track**: Used for fast linting, typechecking, and unit tests WITHOUT spinning up Docker. 2. **Docker Track**: Used for running the application, seeing the UI, background workers, and Postgres. ## Host-Side Track @@ -15,15 +15,20 @@ When you run commands on your local OS (e.g., `just lint`, `just test`, `just fr ## Docker Track When you want to run the app: ```bash -just build # Env-free container build (DOCKER_BUILDKIT=0) -docker compose up -d +just build +just dev ``` -When running the Docker track, all runtime commands must be executed **inside the container**: + +`just dev` runs the full Docker Compose stack in the foreground and keeps streaming service logs. Leave it running in the first terminal. + +Open a second terminal for follow-up commands against the running stack: ```bash -docker compose exec django python manage.py migrate -docker compose exec django python manage.py bootstrap_live_sources +source .venv/bin/activate +just seed ``` +After seeding completes, open in your browser. + ## Celery Beat Schedule The Celery beat schedule file (`celerybeat-schedule`) is written to `.cache/` to prevent dirtying the project root or colliding between host/container environments. @@ -35,4 +40,4 @@ cd frontend && npm run dev ## When to Use Which Workflow * **Writing code, running tests, checking types**: Host-side (`just lint`, `just test`). -* **Testing LLMs, seeing the UI, testing ingestion, full pipelines**: Docker Track (`docker compose up`). +* **Testing LLMs, seeing the UI, testing ingestion, full pipelines**: Docker Track (`just dev`). diff --git a/docs/reference/tunables.md b/docs/reference/tunables.md index eb041eac..375c833e 100644 --- a/docs/reference/tunables.md +++ b/docs/reference/tunables.md @@ -3,12 +3,15 @@ This document collects all parameters, thresholds, and variables that change how the system behaves. Most global tunables are configured via environment variables and loaded into Django settings, while project-specific algorithms use `ProjectConfig`. ## How Settings Are Read + 1. Environment variables set at the Docker Compose / Kubernetes pod level. 2. Loaded in `newsletter_maker/settings/base.py` and combined with defaults. 3. Consumed via `django.conf.settings` across the project. ## LLM & Embeddings -These map directly to global inference capability. + +These map directly to global inference capability. + * `EMBEDDING_PROVIDER`: Options include `local` (HuggingFace `sentence-transformers`), `ollama`, or `openai`/`openrouter`. * `EMBEDDING_MODEL`: The identifier for the dense vector model. * `OLLAMA_URL`: Local instance of Ollama, defaulting to `http://ollama:11434`. @@ -16,35 +19,50 @@ These map directly to global inference capability. * `OPENROUTER_API_BASE`: Endpoint for inference. ## Relevance & Scoring Thresholds + Relevance rules divide candidate articles into clear-match, ambiguous, and clear-non-match bands. See [Algorithms](algorithms.md) for how the pipeline evaluates these. + * **Similarity Thresholds**: Embedding cosine similarity above `0.85` assumes auto-relevant. Below `0.5` assumes irrelevant. The `0.5 - 0.85` band asks the LLM. ## Deduplication Thresholds + * Usually implemented via nearest-neighbor distance (e.g., threshold `< 0.05` means near duplication). ## Authority Weights + Configured per-project in `ProjectConfig`: + * `authority_decay_rate` (default: 0.95): The rate at which an entity's authority metric decays over time without recent mentions. -## Topic Centroid +## Topic Centroid + Configured per-project in `ProjectConfig`: + * `recompute_topic_centroid_on_feedback_save` (default: True): Determines if a user's thumbs up/down immediately recomputes the vector centroid representing the project's topic. ## URL Settings -* `NEWSLETTER_API_BASE_URL`: **Internal API base URL** (e.g. `http://nginx` within Compose) and historically used as a **Public API URL** for generated links (requires external DNS resolution). This is pending split into distinct explicit variables. + +* `NEWSLETTER_API_INTERNAL_URL`: Internal frontend-to-backend base URL used by the Next.js app for API and WebSocket traffic. In Docker Compose this is usually `http://nginx`. +* `NEWSLETTER_PUBLIC_URL`: Public backend base URL used when Django builds absolute links for emails and OAuth callbacks. +* `NEWSLETTER_API_BASE_URL`: Deprecated compatibility fallback. If present, it is used as the default for both explicit URL settings until those are configured separately. * `FRONTEND_BASE_URL`: Where the Next.js app sits. ## Observability Retention + Keeps the database from ballooning over time. + * `OBSERVABILITY_SNAPSHOT_RETENTION_DAYS` (default: 90) * `OBSERVABILITY_TREND_TASK_RUN_RETENTION_DAYS` (default: 30) * `OBSERVABILITY_REVIEW_QUEUE_RETENTION_DAYS` (default: 30) ## OAuth Provider Toggles + Requires specific API keys to be populated to become available: + * **LinkedIn**: `LINKEDIN_CLIENT_ID`, `LINKEDIN_CLIENT_SECRET`, `LINKEDIN_OAUTH_SCOPES` * **Reddit**: `REDDIT_CLIENT_ID`, `REDDIT_CLIENT_SECRET`, `REDDIT_USER_AGENT` ## Channels / Messaging + * `CHANNEL_LAYER_URL`: URL to the Redis instance used by Django Channels for ASGI web socket propagation (e.g., `redis://redis:6379/1`). * `MESSAGING_ENABLED` (frontend/build feature flags). diff --git a/frontend/.env.example b/frontend/.env.example index 345b26a1..3a6dc7f3 100644 --- a/frontend/.env.example +++ b/frontend/.env.example @@ -1,5 +1,5 @@ # Copy this file to .env.local when running the Next.js app outside Docker. -NEWSLETTER_API_BASE_URL=http://127.0.0.1:8080 +NEWSLETTER_API_INTERNAL_URL=http://127.0.0.1:8080 NEWSLETTER_API_USERNAME=admin NEWSLETTER_API_PASSWORD=adminpass NEXT_TELEMETRY_DISABLED=1 diff --git a/frontend/next-env.d.ts b/frontend/next-env.d.ts index 9edff1c7..c4b7818f 100644 --- a/frontend/next-env.d.ts +++ b/frontend/next-env.d.ts @@ -1,6 +1,6 @@ /// /// -import "./.next/types/routes.d.ts"; +import "./.next/dev/types/routes.d.ts"; // NOTE: This file should not be edited // see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/frontend/package.json b/frontend/package.json index 400dbb73..819b076e 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -3,7 +3,7 @@ "private": true, "version": "0.1.0", "scripts": { - "dev": "next dev", + "dev": "next dev --webpack", "build": "next build", "start": "next start", "typecheck": "tsc --noEmit", diff --git a/frontend/src/app/(home)/_components/ContentFeed/index.tsx b/frontend/src/app/(home)/_components/ContentFeed/index.tsx index 558a9227..14749253 100644 --- a/frontend/src/app/(home)/_components/ContentFeed/index.tsx +++ b/frontend/src/app/(home)/_components/ContentFeed/index.tsx @@ -6,7 +6,12 @@ import { Button, buttonVariants } from "@/components/ui/button" import { Card, CardContent } from "@/components/ui/card" import type { Content } from "@/lib/types" import { cn } from "@/lib/utils" -import { formatDate, formatPercentScore, truncateText } from "@/lib/view-helpers" +import { + formatDate, + formatDisplayLabel, + formatPercentScore, + truncateText, +} from "@/lib/view-helpers" import type { ContentClusterBadge } from "../shared" @@ -41,10 +46,10 @@ export function ContentFeed({

{content.title}

-
+
{formatDate(content.published_date)} {content.author || "Unknown author"} - {content.source_plugin} + {formatDisplayLabel(content.source_plugin)}
) : null} - {content.content_type || "unclassified"} + {formatDisplayLabel(content.content_type || "unclassified")} {content.duplicate_signal_count > 0 ? ( @@ -102,7 +107,7 @@ export function ContentFeed({ ) : null}
-

{truncateText(content.content_text)}

+

{truncateText(content.content_text)}

{ />, ) - expect(screen.getByRole("button", { name: "Apply filters" })).toBeInTheDocument() + expect(screen.getByRole("button", { name: "Apply filters" })).toHaveClass( + "hover:bg-primary/84", + ) expect(screen.getByRole("link", { name: "Reset" })).toHaveAttribute("href", "/?project=1") + expect(container.querySelector("#dashboard-view-filter")).toHaveClass( + "border-border/45", + "bg-card/95", + "hover:bg-secondary/88", + ) expect(container.querySelector('input[name="project"]')).toHaveValue("1") expect(container.querySelector('input[name="contentType"]')).toHaveValue("article") expect(container.querySelector('input[name="source"]')).toHaveValue("rss") diff --git a/frontend/src/app/(home)/_components/DashboardFilterToolbar/index.tsx b/frontend/src/app/(home)/_components/DashboardFilterToolbar/index.tsx index 49a4bfe7..e839538a 100644 --- a/frontend/src/app/(home)/_components/DashboardFilterToolbar/index.tsx +++ b/frontend/src/app/(home)/_components/DashboardFilterToolbar/index.tsx @@ -19,6 +19,9 @@ import { duplicateStateOptions, } from "../shared" +const dashboardSelectTriggerClassName = + "w-full rounded-2xl border-border/45 bg-card/95 px-4 py-3 text-foreground shadow-[inset_0_1px_0_rgba(255,255,255,0.06)] hover:bg-secondary/88 focus-visible:border-ring" + type DashboardFilterToolbarProps = { projectId: number view: DashboardView @@ -52,10 +55,7 @@ export function DashboardFilterToolbar({
- + @@ -89,10 +86,7 @@ export function DashboardFilterToolbar({
- + @@ -126,10 +117,7 @@ export function DashboardFilterToolbar({