fix polls API call, move themen back to tabbar, configure only for MdB/MdL
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
import { RepresentativeList } from "@/shared/components/representative-list"
|
||||
import { TopicToggleList } from "@/shared/components/topic-toggle-list"
|
||||
import { useDb } from "@/shared/db/provider"
|
||||
import type { MandateWithPolitician } from "@/shared/lib/aw-api"
|
||||
import { useEffect, useState } from "react"
|
||||
@@ -8,8 +7,6 @@ import { useBundestagUI } from "../store"
|
||||
|
||||
export function BundestagConfigure() {
|
||||
const db = useDb()
|
||||
const topicSearch = useBundestagUI((s) => s.topicSearch)
|
||||
const setTopicSearch = useBundestagUI((s) => s.setTopicSearch)
|
||||
const politicianSearch = useBundestagUI((s) => s.politicianSearch)
|
||||
const setPoliticianSearch = useBundestagUI((s) => s.setPoliticianSearch)
|
||||
|
||||
@@ -25,25 +22,15 @@ export function BundestagConfigure() {
|
||||
|
||||
return (
|
||||
<div className="pb-4">
|
||||
<section>
|
||||
<h2 className="px-4 pt-4 pb-2 text-sm font-semibold text-muted-foreground uppercase tracking-wide">Themen</h2>
|
||||
<TopicToggleList searchQuery={topicSearch} onSearchChange={setTopicSearch} />
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2 className="px-4 pt-4 pb-2 text-sm font-semibold text-muted-foreground uppercase tracking-wide">
|
||||
Abgeordnete
|
||||
</h2>
|
||||
{loadingMandates ? (
|
||||
<div className="flex items-center justify-center h-48">
|
||||
<div className="w-8 h-8 border-4 border-primary border-t-transparent rounded-full animate-spin" />
|
||||
</div>
|
||||
) : mandates.length > 0 ? (
|
||||
<RepresentativeList mandates={mandates} searchQuery={politicianSearch} onSearchChange={setPoliticianSearch} />
|
||||
) : (
|
||||
<p className="px-4 text-sm text-muted-foreground">Keine Abgeordneten verfügbar.</p>
|
||||
)}
|
||||
</section>
|
||||
{loadingMandates ? (
|
||||
<div className="flex items-center justify-center h-48">
|
||||
<div className="w-8 h-8 border-4 border-primary border-t-transparent rounded-full animate-spin" />
|
||||
</div>
|
||||
) : mandates.length > 0 ? (
|
||||
<RepresentativeList mandates={mandates} searchQuery={politicianSearch} onSearchChange={setPoliticianSearch} />
|
||||
) : (
|
||||
<p className="px-4 py-6 text-sm text-muted-foreground text-center">Keine Abgeordneten verfügbar.</p>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2,12 +2,11 @@ import type { FeedItem } from "@/features/feed/lib/assemble-feed"
|
||||
import {
|
||||
type Poll,
|
||||
fetchCandidacyMandates,
|
||||
fetchPolls,
|
||||
fetchPollsByIds,
|
||||
fetchPollsByLegislature,
|
||||
fetchTopics,
|
||||
fetchVotes,
|
||||
} from "@/shared/lib/aw-api"
|
||||
import { BUNDESTAG_LEGISLATURE_ID } from "@/shared/lib/constants"
|
||||
import { fetchUpcomingVorgaenge } from "@/shared/lib/dip-api"
|
||||
|
||||
export async function assembleBundestagFeed(
|
||||
@@ -16,7 +15,7 @@ export async function assembleBundestagFeed(
|
||||
): Promise<FeedItem[]> {
|
||||
const [topics, polls, vorgaenge] = await Promise.all([
|
||||
fetchTopics(),
|
||||
fetchPollsByLegislature(BUNDESTAG_LEGISLATURE_ID, 150),
|
||||
fetchPolls(150),
|
||||
fetchUpcomingVorgaenge().catch(() => []),
|
||||
])
|
||||
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
import { create } from "zustand"
|
||||
|
||||
interface BundestagUIState {
|
||||
topicSearch: string
|
||||
setTopicSearch: (query: string) => void
|
||||
politicianSearch: string
|
||||
setPoliticianSearch: (query: string) => void
|
||||
}
|
||||
|
||||
export const useBundestagUI = create<BundestagUIState>((set) => ({
|
||||
topicSearch: "",
|
||||
setTopicSearch: (query) => set({ topicSearch: query }),
|
||||
politicianSearch: "",
|
||||
setPoliticianSearch: (query) => set({ politicianSearch: query }),
|
||||
}))
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { RepresentativeList } from "@/shared/components/representative-list"
|
||||
import { TopicToggleList } from "@/shared/components/topic-toggle-list"
|
||||
import { useDb } from "@/shared/db/provider"
|
||||
import type { MandateWithPolitician } from "@/shared/lib/aw-api"
|
||||
import { Link } from "@tanstack/react-router"
|
||||
@@ -9,10 +8,8 @@ import { useLandtagUI } from "../store"
|
||||
|
||||
export function LandtagConfigure() {
|
||||
const db = useDb()
|
||||
const topicSearch = useLandtagUI((s) => s.topicSearch)
|
||||
const setTopicSearch = useLandtagUI((s) => s.setTopicSearch)
|
||||
const politicianSearch = useLandtagUI((s) => s.politicianSearch)
|
||||
const setPoliticianSearch = useLandtagUI((s) => s.setPoliticianSearch)
|
||||
const search = useLandtagUI((s) => s.politicianSearch)
|
||||
const setSearch = useLandtagUI((s) => s.setPoliticianSearch)
|
||||
const [mandates, setMandates] = useState<MandateWithPolitician[]>([])
|
||||
const [loaded, setLoaded] = useState(false)
|
||||
|
||||
@@ -23,34 +20,28 @@ export function LandtagConfigure() {
|
||||
})
|
||||
}, [db])
|
||||
|
||||
if (loaded && mandates.length === 0) {
|
||||
return (
|
||||
<div className="text-center mt-12 px-4">
|
||||
<p className="text-muted-foreground text-sm mb-4">
|
||||
Noch keine Abgeordneten geladen. Erkenne zuerst deinen Standort in den Einstellungen.
|
||||
</p>
|
||||
<Link to="/app/settings" className="text-primary text-sm underline">
|
||||
Zu den Einstellungen
|
||||
</Link>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="pb-4">
|
||||
<section>
|
||||
<h2 className="px-4 pt-4 pb-2 text-sm font-semibold text-muted-foreground uppercase tracking-wide">Themen</h2>
|
||||
<TopicToggleList searchQuery={topicSearch} onSearchChange={setTopicSearch} />
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2 className="px-4 pt-4 pb-2 text-sm font-semibold text-muted-foreground uppercase tracking-wide">
|
||||
Abgeordnete
|
||||
</h2>
|
||||
{!loaded ? (
|
||||
<div className="flex items-center justify-center h-48">
|
||||
<div className="w-8 h-8 border-4 border-primary border-t-transparent rounded-full animate-spin" />
|
||||
</div>
|
||||
) : mandates.length > 0 ? (
|
||||
<RepresentativeList mandates={mandates} searchQuery={politicianSearch} onSearchChange={setPoliticianSearch} />
|
||||
) : (
|
||||
<div className="text-center px-4 py-6">
|
||||
<p className="text-muted-foreground text-sm mb-4">
|
||||
Noch keine Abgeordneten geladen. Erkenne zuerst deinen Standort in den Einstellungen.
|
||||
</p>
|
||||
<Link to="/app/settings" className="text-primary text-sm underline">
|
||||
Zu den Einstellungen
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
{!loaded ? (
|
||||
<div className="flex items-center justify-center h-48">
|
||||
<div className="w-8 h-8 border-4 border-primary border-t-transparent rounded-full animate-spin" />
|
||||
</div>
|
||||
) : (
|
||||
<RepresentativeList mandates={mandates} searchQuery={search} onSearchChange={setSearch} />
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,55 +1,8 @@
|
||||
import { RepresentativeList } from "@/shared/components/representative-list"
|
||||
import { useDb } from "@/shared/db/provider"
|
||||
import type { MandateWithPolitician } from "@/shared/lib/aw-api"
|
||||
import { Link } from "@tanstack/react-router"
|
||||
import { useEffect, useState } from "react"
|
||||
import { type GeoResult, loadCachedResult } from "../../location/lib/geo"
|
||||
import { useLandtagUI } from "../store"
|
||||
|
||||
export function LandtagPage() {
|
||||
const db = useDb()
|
||||
const [result, setResult] = useState<GeoResult | null>(null)
|
||||
const [mandates, setMandates] = useState<MandateWithPolitician[]>([])
|
||||
const search = useLandtagUI((s) => s.politicianSearch)
|
||||
const setSearch = useLandtagUI((s) => s.setPoliticianSearch)
|
||||
|
||||
useEffect(() => {
|
||||
loadCachedResult(db).then((cached) => {
|
||||
if (cached) {
|
||||
setResult(cached)
|
||||
setMandates(cached.mandates)
|
||||
}
|
||||
})
|
||||
}, [db])
|
||||
|
||||
if (!result || mandates.length === 0) {
|
||||
return (
|
||||
<div className="text-center mt-12 px-4">
|
||||
<p className="text-lg font-medium">Landtag</p>
|
||||
<p className="text-muted-foreground text-sm mt-2 mb-4">
|
||||
Erkenne zuerst deinen Standort in den Einstellungen, um deine Landtagsabgeordneten zu sehen.
|
||||
</p>
|
||||
<Link to="/app/settings" className="text-primary text-sm underline">
|
||||
Zu den Einstellungen
|
||||
</Link>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="pb-4">
|
||||
{result.landtag_label && (
|
||||
<div className="px-4 py-3 border-b border-border">
|
||||
<p className="text-sm font-medium">{result.landtag_label}</p>
|
||||
{result.bundesland && <p className="text-xs text-muted-foreground">{result.bundesland}</p>}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<RepresentativeList mandates={mandates} searchQuery={search} onSearchChange={setSearch} />
|
||||
|
||||
<div className="px-4 py-6 text-center">
|
||||
<p className="text-sm text-muted-foreground">Abstimmungsdaten folgen in Kürze</p>
|
||||
</div>
|
||||
<div className="text-center mt-12 px-4">
|
||||
<p className="text-lg font-medium">Landtag</p>
|
||||
<p className="text-sm text-muted-foreground mt-2">Abstimmungsdaten folgen in Kürze.</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
import { create } from "zustand"
|
||||
|
||||
interface LandtagUIState {
|
||||
topicSearch: string
|
||||
setTopicSearch: (query: string) => void
|
||||
politicianSearch: string
|
||||
setPoliticianSearch: (query: string) => void
|
||||
}
|
||||
|
||||
export const useLandtagUI = create<LandtagUIState>((set) => ({
|
||||
topicSearch: "",
|
||||
setTopicSearch: (query) => set({ topicSearch: query }),
|
||||
politicianSearch: "",
|
||||
setPoliticianSearch: (query) => set({ politicianSearch: query }),
|
||||
}))
|
||||
|
||||
9
src/features/topics/components/topic-list.tsx
Normal file
9
src/features/topics/components/topic-list.tsx
Normal file
@@ -0,0 +1,9 @@
|
||||
import { TopicToggleList } from "@/shared/components/topic-toggle-list"
|
||||
import { useTopicsUI } from "../store"
|
||||
|
||||
export function TopicList() {
|
||||
const search = useTopicsUI((s) => s.searchQuery)
|
||||
const setSearch = useTopicsUI((s) => s.setSearchQuery)
|
||||
|
||||
return <TopicToggleList searchQuery={search} onSearchChange={setSearch} />
|
||||
}
|
||||
1
src/features/topics/index.ts
Normal file
1
src/features/topics/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { TopicList } from "./components/topic-list"
|
||||
11
src/features/topics/store.ts
Normal file
11
src/features/topics/store.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { create } from "zustand"
|
||||
|
||||
interface TopicsUIState {
|
||||
searchQuery: string
|
||||
setSearchQuery: (query: string) => void
|
||||
}
|
||||
|
||||
export const useTopicsUI = create<TopicsUIState>((set) => ({
|
||||
searchQuery: "",
|
||||
setSearchQuery: (query) => set({ searchQuery: query }),
|
||||
}))
|
||||
@@ -11,6 +11,7 @@
|
||||
import { Route as rootRouteImport } from './routes/__root'
|
||||
import { Route as AppRouteRouteImport } from './routes/app/route'
|
||||
import { Route as IndexRouteImport } from './routes/index'
|
||||
import { Route as AppTopicsRouteImport } from './routes/app/topics'
|
||||
import { Route as AppSettingsRouteImport } from './routes/app/settings'
|
||||
import { Route as AppHomeRouteImport } from './routes/app/home'
|
||||
import { Route as AppLandtagRouteRouteImport } from './routes/app/landtag/route'
|
||||
@@ -30,6 +31,11 @@ const IndexRoute = IndexRouteImport.update({
|
||||
path: '/',
|
||||
getParentRoute: () => rootRouteImport,
|
||||
} as any)
|
||||
const AppTopicsRoute = AppTopicsRouteImport.update({
|
||||
id: '/topics',
|
||||
path: '/topics',
|
||||
getParentRoute: () => AppRouteRoute,
|
||||
} as any)
|
||||
const AppSettingsRoute = AppSettingsRouteImport.update({
|
||||
id: '/settings',
|
||||
path: '/settings',
|
||||
@@ -78,6 +84,7 @@ export interface FileRoutesByFullPath {
|
||||
'/app/landtag': typeof AppLandtagRouteRouteWithChildren
|
||||
'/app/home': typeof AppHomeRoute
|
||||
'/app/settings': typeof AppSettingsRoute
|
||||
'/app/topics': typeof AppTopicsRoute
|
||||
'/app/bundestag/configure': typeof AppBundestagConfigureRoute
|
||||
'/app/landtag/configure': typeof AppLandtagConfigureRoute
|
||||
'/app/bundestag/': typeof AppBundestagIndexRoute
|
||||
@@ -88,6 +95,7 @@ export interface FileRoutesByTo {
|
||||
'/app': typeof AppRouteRouteWithChildren
|
||||
'/app/home': typeof AppHomeRoute
|
||||
'/app/settings': typeof AppSettingsRoute
|
||||
'/app/topics': typeof AppTopicsRoute
|
||||
'/app/bundestag/configure': typeof AppBundestagConfigureRoute
|
||||
'/app/landtag/configure': typeof AppLandtagConfigureRoute
|
||||
'/app/bundestag': typeof AppBundestagIndexRoute
|
||||
@@ -101,6 +109,7 @@ export interface FileRoutesById {
|
||||
'/app/landtag': typeof AppLandtagRouteRouteWithChildren
|
||||
'/app/home': typeof AppHomeRoute
|
||||
'/app/settings': typeof AppSettingsRoute
|
||||
'/app/topics': typeof AppTopicsRoute
|
||||
'/app/bundestag/configure': typeof AppBundestagConfigureRoute
|
||||
'/app/landtag/configure': typeof AppLandtagConfigureRoute
|
||||
'/app/bundestag/': typeof AppBundestagIndexRoute
|
||||
@@ -115,6 +124,7 @@ export interface FileRouteTypes {
|
||||
| '/app/landtag'
|
||||
| '/app/home'
|
||||
| '/app/settings'
|
||||
| '/app/topics'
|
||||
| '/app/bundestag/configure'
|
||||
| '/app/landtag/configure'
|
||||
| '/app/bundestag/'
|
||||
@@ -125,6 +135,7 @@ export interface FileRouteTypes {
|
||||
| '/app'
|
||||
| '/app/home'
|
||||
| '/app/settings'
|
||||
| '/app/topics'
|
||||
| '/app/bundestag/configure'
|
||||
| '/app/landtag/configure'
|
||||
| '/app/bundestag'
|
||||
@@ -137,6 +148,7 @@ export interface FileRouteTypes {
|
||||
| '/app/landtag'
|
||||
| '/app/home'
|
||||
| '/app/settings'
|
||||
| '/app/topics'
|
||||
| '/app/bundestag/configure'
|
||||
| '/app/landtag/configure'
|
||||
| '/app/bundestag/'
|
||||
@@ -164,6 +176,13 @@ declare module '@tanstack/react-router' {
|
||||
preLoaderRoute: typeof IndexRouteImport
|
||||
parentRoute: typeof rootRouteImport
|
||||
}
|
||||
'/app/topics': {
|
||||
id: '/app/topics'
|
||||
path: '/topics'
|
||||
fullPath: '/app/topics'
|
||||
preLoaderRoute: typeof AppTopicsRouteImport
|
||||
parentRoute: typeof AppRouteRoute
|
||||
}
|
||||
'/app/settings': {
|
||||
id: '/app/settings'
|
||||
path: '/settings'
|
||||
@@ -255,6 +274,7 @@ interface AppRouteRouteChildren {
|
||||
AppLandtagRouteRoute: typeof AppLandtagRouteRouteWithChildren
|
||||
AppHomeRoute: typeof AppHomeRoute
|
||||
AppSettingsRoute: typeof AppSettingsRoute
|
||||
AppTopicsRoute: typeof AppTopicsRoute
|
||||
}
|
||||
|
||||
const AppRouteRouteChildren: AppRouteRouteChildren = {
|
||||
@@ -262,6 +282,7 @@ const AppRouteRouteChildren: AppRouteRouteChildren = {
|
||||
AppLandtagRouteRoute: AppLandtagRouteRouteWithChildren,
|
||||
AppHomeRoute: AppHomeRoute,
|
||||
AppSettingsRoute: AppSettingsRoute,
|
||||
AppTopicsRoute: AppTopicsRoute,
|
||||
}
|
||||
|
||||
const AppRouteRouteWithChildren = AppRouteRoute._addFileChildren(
|
||||
|
||||
@@ -24,6 +24,11 @@ const TABS: TabDef[] = [
|
||||
label: "Landtag",
|
||||
icon: "M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0z",
|
||||
},
|
||||
{
|
||||
to: "/app/topics",
|
||||
label: "Themen",
|
||||
icon: "M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2",
|
||||
},
|
||||
{
|
||||
to: "/app/settings",
|
||||
label: "Einstellungen",
|
||||
@@ -87,7 +92,7 @@ function AppLayout() {
|
||||
</button>
|
||||
) : null}
|
||||
<h1 className={`text-base font-semibold text-card-foreground ${isConfigureRoute ? "ml-2" : ""}`}>
|
||||
{isConfigureRoute ? `${currentTab.label} konfigurieren` : currentTab.label}
|
||||
{isConfigureRoute ? "Abgeordnete" : currentTab.label}
|
||||
</h1>
|
||||
{!isConfigureRoute && configureTarget && (
|
||||
<Link
|
||||
|
||||
6
src/routes/app/topics.tsx
Normal file
6
src/routes/app/topics.tsx
Normal file
@@ -0,0 +1,6 @@
|
||||
import { TopicList } from "@/features/topics"
|
||||
import { createFileRoute } from "@tanstack/react-router"
|
||||
|
||||
export const Route = createFileRoute("/app/topics")({
|
||||
component: TopicList,
|
||||
})
|
||||
@@ -165,19 +165,6 @@ export function fetchVotes(mandateID: number): Promise<Vote[]> {
|
||||
return request("votes", { mandate: String(mandateID), range_end: "200" }, voteSchema)
|
||||
}
|
||||
|
||||
export function fetchPollsByLegislature(legislatureId: number, rangeEnd = 150): Promise<Poll[]> {
|
||||
return request(
|
||||
"polls",
|
||||
{
|
||||
parliament_period: String(legislatureId),
|
||||
range_end: String(rangeEnd),
|
||||
sort_by: "field_poll_date",
|
||||
sort_direction: "desc",
|
||||
},
|
||||
pollSchema,
|
||||
)
|
||||
}
|
||||
|
||||
export function fetchMandatesByParliamentPeriod(periodID: number): Promise<MandateWithPolitician[]> {
|
||||
return request(
|
||||
"candidacies-mandates",
|
||||
|
||||
Reference in New Issue
Block a user