From 45fe15eef2acb57851ebfaa793561b147b48eba5 Mon Sep 17 00:00:00 2001 From: Oskar-Mikael Date: Sat, 27 Dec 2025 16:51:57 +0100 Subject: [PATCH] Add new models and page for motions --- .prettierrc | 13 + app/Console/Commands/ImportRiksdagData.php | 93 ++ app/Jobs/ImportRiksdagDataJob.php | 562 +++++++++++ app/Livewire/Motion/Search.php | 146 +++ app/Livewire/Motion/Show.php | 71 ++ app/Livewire/Person/Search.php | 13 +- app/Livewire/Person/Show.php | 97 +- app/Models/Anforande.php | 20 + app/Models/Debatt.php | 20 + app/Models/DokAktivitet.php | 27 + app/Models/DokBilaga.php | 28 + app/Models/DokForslag.php | 34 + app/Models/DokIntressent.php | 31 + app/Models/DokMotforslag.php | 15 + app/Models/DokReferens.php | 31 + app/Models/DokUppgift.php | 30 + app/Models/DokUtskottsforslag.php | 15 + app/Models/Dokument.php | 102 ++ app/Models/Organ.php | 31 + app/Models/Person.php | 61 ++ app/Models/PersonUppdrag.php | 44 + app/Models/PersonUppgift.php | 24 + app/Models/Planering.php | 31 + app/Models/Riksmote.php | 16 + app/Models/Roll.php | 16 + app/Models/Votering.php | 20 + app/Services/RiksdagenService.php | 90 +- ...5_12_27_000000_create_riksdagen_tables.php | 340 +++++++ package-lock.json | 33 + package.json | 3 + resources/views/livewire/home-page.blade.php | 239 +++-- .../views/livewire/motion/search.blade.php | 361 +++++++ .../views/livewire/motion/show.blade.php | 486 ++++++++++ .../views/livewire/person/search.blade.php | 65 +- .../views/livewire/person/show.blade.php | 888 +++++++++++++----- routes/web.php | 12 +- 36 files changed, 3753 insertions(+), 355 deletions(-) create mode 100644 .prettierrc create mode 100644 app/Console/Commands/ImportRiksdagData.php create mode 100644 app/Jobs/ImportRiksdagDataJob.php create mode 100644 app/Livewire/Motion/Search.php create mode 100644 app/Livewire/Motion/Show.php create mode 100644 app/Models/Anforande.php create mode 100644 app/Models/Debatt.php create mode 100644 app/Models/DokAktivitet.php create mode 100644 app/Models/DokBilaga.php create mode 100644 app/Models/DokForslag.php create mode 100644 app/Models/DokIntressent.php create mode 100644 app/Models/DokMotforslag.php create mode 100644 app/Models/DokReferens.php create mode 100644 app/Models/DokUppgift.php create mode 100644 app/Models/DokUtskottsforslag.php create mode 100644 app/Models/Dokument.php create mode 100644 app/Models/Organ.php create mode 100644 app/Models/Person.php create mode 100644 app/Models/PersonUppdrag.php create mode 100644 app/Models/PersonUppgift.php create mode 100644 app/Models/Planering.php create mode 100644 app/Models/Riksmote.php create mode 100644 app/Models/Roll.php create mode 100644 app/Models/Votering.php create mode 100644 database/migrations/2025_12_27_000000_create_riksdagen_tables.php create mode 100644 resources/views/livewire/motion/search.blade.php create mode 100644 resources/views/livewire/motion/show.blade.php diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..ed3b5ef --- /dev/null +++ b/.prettierrc @@ -0,0 +1,13 @@ +{ + "plugins": ["prettier-plugin-blade"], + "overrides": [ + { + "files": ["*.blade.php"], + "options": { + "parser": "blade" + } + } + ], + "singleQuote": true, + "tabWidth": 4 +} diff --git a/app/Console/Commands/ImportRiksdagData.php b/app/Console/Commands/ImportRiksdagData.php new file mode 100644 index 0000000..5740661 --- /dev/null +++ b/app/Console/Commands/ImportRiksdagData.php @@ -0,0 +1,93 @@ +argument('file'); + $url = $this->option('url'); + + if (! $filePath && ! $url) { + $this->error('Either provide a file path or use --url option'); + + return 1; + } + + if ($url) { + // Process ZIP from URL + $this->info("Starting ZIP download and import from: {$url}"); + + if ($this->option('queue')) { + ImportRiksdagDataJob::dispatch($url, true); + $this->info('ZIP import job has been queued. Check queue workers for progress.'); + } else { + try { + $job = new ImportRiksdagDataJob($url, true); + $job->handle(); + $this->info('ZIP import completed successfully!'); + } catch (\Exception $e) { + $this->error('ZIP import failed: '.$e->getMessage()); + + return 1; + } + } + } else { + // Process single file + if (! file_exists($filePath)) { + $this->error("File not found: {$filePath}"); + + return 1; + } + + // Validate JSON format + $jsonContent = file_get_contents($filePath); + $testDecode = json_decode($jsonContent, true); + + if (json_last_error() !== JSON_ERROR_NONE) { + $this->error('Invalid JSON format: '.json_last_error_msg()); + + return 1; + } + + $this->info("Starting import from: {$filePath}"); + + if ($this->option('queue')) { + ImportRiksdagDataJob::dispatch($filePath, false); + $this->info('Import job has been queued. Check queue workers for progress.'); + } else { + try { + $job = new ImportRiksdagDataJob($filePath, false); + $job->handle(); + $this->info('Import completed successfully!'); + } catch (\Exception $e) { + $this->error('Import failed: '.$e->getMessage()); + + return 1; + } + } + } + + return 0; + } +} diff --git a/app/Jobs/ImportRiksdagDataJob.php b/app/Jobs/ImportRiksdagDataJob.php new file mode 100644 index 0000000..d463c4e --- /dev/null +++ b/app/Jobs/ImportRiksdagDataJob.php @@ -0,0 +1,562 @@ +url = $urlOrPath; + $this->isUrl = $isUrl; + $this->filePath = $urlOrPath; + } + + public function handle() + { + try { + if ($this->isUrl) { + $this->processZipFromUrl(); + } else { + $this->processSingleFile($this->filePath); + } + } catch (\Exception $e) { + Log::error('Error in ImportRiksdagDataJob: '.$e->getMessage()); + throw $e; + } + } + + protected function processZipFromUrl() + { + Log::info('Starting download from: '.$this->url); + + // Download the ZIP file + $response = Http::timeout(300)->get($this->url); + + if (! $response->successful()) { + throw new \Exception('Failed to download ZIP file from: '.$this->url); + } + + // Create temporary directory + $tempDir = storage_path('app/temp/riksdag_import_'.time()); + $zipPath = $tempDir.'/download.zip'; + + if (! is_dir($tempDir)) { + mkdir($tempDir, 0755, true); + } + + // Save ZIP file + file_put_contents($zipPath, $response->body()); + Log::info('ZIP file downloaded to: '.$zipPath); + + // Extract ZIP file + $extractPath = $tempDir.'/extracted'; + $this->extractZipFile($zipPath, $extractPath); + + // Process all JSON files in extracted directory + $this->processJsonFiles($extractPath); + + // Clean up temporary files + $this->cleanupTempDirectory($tempDir); + + Log::info('ZIP processing completed successfully'); + } + + protected function extractZipFile($zipPath, $extractPath) + { + $zip = new ZipArchive; + $result = $zip->open($zipPath); + + if ($result !== true) { + throw new \Exception('Failed to open ZIP file: '.$zipPath.' (Error code: '.$result.')'); + } + + if (! is_dir($extractPath)) { + mkdir($extractPath, 0755, true); + } + + $zip->extractTo($extractPath); + $zip->close(); + + Log::info('ZIP file extracted to: '.$extractPath); + } + + protected function processJsonFiles($directory) + { + $jsonFiles = glob($directory.'/*.json'); + + if (empty($jsonFiles)) { + // Check subdirectories + $subdirs = glob($directory.'/*', GLOB_ONLYDIR); + foreach ($subdirs as $subdir) { + $jsonFiles = array_merge($jsonFiles, glob($subdir.'/*.json')); + } + } + + Log::info('Found '.count($jsonFiles).' JSON files to process'); + + foreach ($jsonFiles as $jsonFile) { + Log::info('Processing file: '.basename($jsonFile)); + $this->processSingleFile($jsonFile); + } + } + + protected function cleanupTempDirectory($tempDir) + { + $this->deleteDirectory($tempDir); + Log::info('Temporary directory cleaned up: '.$tempDir); + } + + protected function deleteDirectory($dir) + { + if (! is_dir($dir)) { + return; + } + + $files = array_diff(scandir($dir), ['.', '..']); + foreach ($files as $file) { + $path = $dir.'/'.$file; + is_dir($path) ? $this->deleteDirectory($path) : unlink($path); + } + rmdir($dir); + } + + protected function processSingleFile($filePath) + { + try { + $jsonContent = file_get_contents($filePath); + $data = json_decode($jsonContent, true); + + if (json_last_error() !== JSON_ERROR_NONE) { + throw new \Exception('Invalid JSON format: '.json_last_error_msg()); + } + + DB::beginTransaction(); + + // Handle new JSON structure with dokumentstatus wrapper + if (isset($data['dokumentstatus'])) { + $this->processDocumentStatus($data['dokumentstatus']); + } + // Legacy support for old structure + elseif (isset($data['dokumentlista'])) { + $this->importDokumentData($data['dokumentlista']); + } elseif (isset($data['personlista'])) { + $this->importPersonData($data['personlista']); + } elseif (isset($data['organlista'])) { + $this->importOrganData($data['organlista']); + } + // Handle single document structure + elseif (isset($data['dokument'])) { + $this->importSingleDokument($data['dokument']); + } + + DB::commit(); + Log::info('Successfully imported data from: '.$filePath); + + } catch (\Exception $e) { + DB::rollBack(); + Log::error("Error importing data from {$filePath}: ".$e->getMessage()); + throw $e; + } + } + + protected function processDocumentStatus($dokumentStatus) + { + // Process the main document + if (isset($dokumentStatus['dokument'])) { + $this->processDokument($dokumentStatus['dokument']); + } + + // Get hangar_id for related data processing + $hangarId = $dokumentStatus['dokument']['hangar_id'] ?? null; + + if (! $hangarId) { + throw new \Exception('No hangar_id found in document data'); + } + + // Process document proposals (forslag) + if (isset($dokumentStatus['dokforslag']['forslag']) && is_array($dokumentStatus['dokforslag']['forslag'])) { + $this->processForslag($hangarId, $dokumentStatus['dokforslag']['forslag']); + } + + // Process document activities (aktivitet) + if (isset($dokumentStatus['dokaktivitet']['aktivitet']) && is_array($dokumentStatus['dokaktivitet']['aktivitet'])) { + $this->processAktiviteter($hangarId, $dokumentStatus['dokaktivitet']['aktivitet']); + } + + // Process document interested parties (intressent) + if (isset($dokumentStatus['dokintressent']['intressent']) && is_array($dokumentStatus['dokintressent']['intressent'])) { + $this->processIntressenter($hangarId, $dokumentStatus['dokintressent']['intressent']); + } + + // Process document information (uppgift) + if (isset($dokumentStatus['dokuppgift']['uppgift']) && is_array($dokumentStatus['dokuppgift']['uppgift'])) { + $this->processUppgifter($hangarId, $dokumentStatus['dokuppgift']['uppgift']); + } + + // Process document attachments (bilaga) + if (isset($dokumentStatus['dokbilaga']['bilaga']) && is_array($dokumentStatus['dokbilaga']['bilaga'])) { + $this->processBilagor($hangarId, $dokumentStatus['dokbilaga']['bilaga']); + } + + // Process document references (referens) + if (isset($dokumentStatus['dokreferens']['referens']) && is_array($dokumentStatus['dokreferens']['referens'])) { + $this->processReferenser($hangarId, $dokumentStatus['dokreferens']['referens']); + } + } + + protected function importDokumentData($dokumentData) + { + $documents = isset($dokumentData['dokument']) ? $dokumentData['dokument'] : []; + + // Handle both single document and array of documents + if (! is_array($documents) || (isset($documents['hangar_id']) && ! is_numeric(array_keys($documents)[0]))) { + $documents = [$documents]; + } + + foreach ($documents as $dokData) { + $this->processDokument($dokData); + } + } + + protected function processDokument($dokData) + { + // Create main document + $dokument = Dokument::updateOrCreate( + ['dok_id' => $dokData['dok_id'] ?? null], + [ + 'hangar_id' => $dokData['hangar_id'] ?? null, + 'rm' => $dokData['rm'] ?? null, + 'beteckning' => $dokData['beteckning'] ?? null, + 'typ' => $dokData['typ'] ?? null, + 'subtyp' => $dokData['subtyp'] ?? null, + 'doktyp' => $dokData['doktyp'] ?? null, + 'typrubrik' => $dokData['typrubrik'] ?? null, + 'dokumentnamn' => $dokData['dokumentnamn'] ?? null, + 'debattnamn' => $dokData['debattnamn'] ?? null, + 'tempbeteckning' => $dokData['tempbeteckning'] ?? null, + 'organ' => $dokData['organ'] ?? null, + 'mottagare' => $dokData['mottagare'] ?? null, + 'nummer' => $dokData['nummer'] ?? null, + 'slutnummer' => $dokData['slutnummer'] ?? null, + 'datum' => isset($dokData['datum']) ? $this->parseDate($dokData['datum']) : null, + 'systemdatum' => isset($dokData['systemdatum']) ? $this->parseDate($dokData['systemdatum']) : null, + 'publicerad' => isset($dokData['publicerad']) ? $this->parseDate($dokData['publicerad']) : null, + 'titel' => $dokData['titel'] ?? null, + 'subtitel' => $dokData['subtitel'] ?? null, + 'status' => $dokData['status'] ?? null, + 'htmlformat' => $dokData['htmlformat'] ?? null, + 'relaterat_id' => $dokData['relaterat_id'] ?? null, + 'source' => $dokData['source'] ?? null, + 'sourceid' => $dokData['sourceid'] ?? null, + 'dokument_url_text' => $dokData['dokument_url_text'] ?? null, + 'dokument_url_html' => $dokData['dokument_url_html'] ?? null, + 'dokumentstatus_url_xml' => $dokData['dokumentstatus_url_xml'] ?? null, + 'utskottsforslag_url_xml' => $dokData['utskottsforslag_url_xml'] ?? null, + ] + ); + + // Process related data + if (isset($dokData['aktivitet']) && is_array($dokData['aktivitet'])) { + $this->processAktiviteter($dokument->hangar_id, $dokData['aktivitet']); + } + + if (isset($dokData['intressent']) && is_array($dokData['intressent'])) { + $this->processIntressenter($dokument->hangar_id, $dokData['intressent']); + } + + if (isset($dokData['bilaga']) && is_array($dokData['bilaga'])) { + $this->processBilagor($dokument->hangar_id, $dokData['bilaga']); + } + + if (isset($dokData['forslag']) && is_array($dokData['forslag'])) { + $this->processForslag($dokument->hangar_id, $dokData['forslag']); + } + + if (isset($dokData['uppgift']) && is_array($dokData['uppgift'])) { + $this->processUppgifter($dokument->hangar_id, $dokData['uppgift']); + } + + if (isset($dokData['referens']) && is_array($dokData['referens'])) { + $this->processReferenser($dokument->hangar_id, $dokData['referens']); + } + } + + protected function processAktiviteter($hangarId, $aktiviteter) + { + foreach ($aktiviteter as $aktivitet) { + DokAktivitet::updateOrCreate( + [ + 'hangar_id' => $hangarId, + 'kod' => $aktivitet['kod'] ?? null, + 'datum' => isset($aktivitet['datum']) ? $this->parseDate($aktivitet['datum']) : null, + ], + [ + 'namn' => $aktivitet['namn'] ?? null, + 'status' => $aktivitet['status'] ?? null, + 'ordning' => $aktivitet['ordning'] ?? null, + 'process' => $aktivitet['process'] ?? null, + ] + ); + } + } + + protected function processIntressenter($hangarId, $intressenter) + { + foreach ($intressenter as $intressent) { + dump($intressent); + DokIntressent::updateOrCreate( + [ + 'hangar_id' => $hangarId, + 'intressent_id' => $intressent['intressent_id'] ?? null, + ], + [ + 'namn' => $intressent['namn'] ?? null, + 'partibet' => $intressent['partibet'] ?? null, + 'ordning' => $intressent['ordning'] ?? null, + 'roll' => $intressent['roll'] ?? null, + ] + ); + } + } + + protected function processBilagor($hangarId, $bilagor) + { + foreach ($bilagor as $bilaga) { + DokBilaga::updateOrCreate( + [ + 'hangar_id' => $hangarId, + 'dok_id' => $bilaga['dok_id'] ?? null, + 'filnamn' => $bilaga['filnamn'] ?? null, + ], + [ + 'titel' => $bilaga['titel'] ?? null, + 'subtitel' => $bilaga['subtitel'] ?? null, + 'filstorlek' => $bilaga['filstorlek'] ?? null, + 'filtyp' => $bilaga['filtyp'] ?? null, + 'fil_url' => $bilaga['fil_url'] ?? null, + ] + ); + } + } + + protected function processForslag($hangarId, $forslag) + { + foreach ($forslag as $forslagItem) { + DokForslag::updateOrCreate( + [ + 'hangar_id' => $hangarId, + 'nummer' => $forslagItem['nummer'] ?? null, + ], + [ + 'beteckning' => $forslagItem['beteckning'] ?? null, + 'lydelse' => $forslagItem['lydelse'] ?? null, + 'lydelse2' => $forslagItem['lydelse2'] ?? null, + 'utskottet' => $forslagItem['utskottet'] ?? null, + 'kammaren' => $forslagItem['kammaren'] ?? null, + 'behandlas_i' => $forslagItem['behandlas_i'] ?? null, + 'behandlas_i_punkt' => $forslagItem['behandlas_i_punkt'] ?? null, + 'kammarbeslutstyp' => $forslagItem['kammarbeslutstyp'] ?? null, + 'intressent' => $forslagItem['intressent'] ?? null, + 'avsnitt' => $forslagItem['avsnitt'] ?? null, + 'grundforfattning' => $forslagItem['grundforfattning'] ?? null, + 'andringsforfattning' => $forslagItem['andringsforfattning'] ?? null, + ] + ); + } + } + + protected function processUppgifter($hangarId, $uppgifter) + { + foreach ($uppgifter as $uppgift) { + DokUppgift::updateOrCreate( + [ + 'hangar_id' => $hangarId, + 'kod' => $uppgift['kod'] ?? null, + ], + [ + 'namn' => $uppgift['namn'] ?? null, + 'text' => $uppgift['text'] ?? null, + 'dok_id' => $uppgift['dok_id'] ?? null, + 'systemdatum' => isset($uppgift['systemdatum']) ? $this->parseDate($uppgift['systemdatum']) : null, + ] + ); + } + } + + protected function processReferenser($hangarId, $referenser) + { + foreach ($referenser as $referens) { + DokReferens::updateOrCreate( + [ + 'hangar_id' => $hangarId, + 'referenstyp' => $referens['referenstyp'] ?? null, + 'ref_dok_id' => $referens['ref_dok_id'] ?? null, + ], + [ + 'uppgift' => $referens['uppgift'] ?? null, + 'ref_dok_typ' => $referens['ref_dok_typ'] ?? null, + 'ref_dok_rm' => $referens['ref_dok_rm'] ?? null, + 'ref_dok_bet' => $referens['ref_dok_bet'] ?? null, + 'ref_dok_titel' => $referens['ref_dok_titel'] ?? null, + 'ref_dok_subtitel' => $referens['ref_dok_subtitel'] ?? null, + 'ref_dok_subtyp' => $referens['ref_dok_subtyp'] ?? null, + 'ref_dok_dokumentnamn' => $referens['ref_dok_dokumentnamn'] ?? null, + ] + ); + } + } + + protected function importPersonData($personData) + { + $persons = isset($personData['person']) ? $personData['person'] : []; + + // Handle both single person and array of persons + if (! is_array($persons) || (isset($persons['intressent_id']) && ! is_numeric(array_keys($persons)[0]))) { + $persons = [$persons]; + } + + foreach ($persons as $personInfo) { + $this->processPerson($personInfo); + } + } + + protected function processPerson($personInfo) + { + $person = Person::updateOrCreate( + ['intressent_id' => $personInfo['intressent_id'] ?? null], + [ + 'född_år' => $personInfo['fodd_ar'] ?? $personInfo['född_år'] ?? null, + 'kön' => $personInfo['kon'] ?? $personInfo['kön'] ?? null, + 'efternamn' => $personInfo['efternamn'] ?? null, + 'tilltalsnamn' => $personInfo['tilltalsnamn'] ?? null, + 'sorteringsnamn' => $personInfo['sorteringsnamn'] ?? null, + 'iort' => $personInfo['iort'] ?? null, + 'parti' => $personInfo['parti'] ?? null, + 'valkrets' => $personInfo['valkrets'] ?? null, + 'status' => $personInfo['status'] ?? null, + ] + ); + + // Process person assignments + if (isset($personInfo['personuppdrag']['uppdrag']) && is_array($personInfo['personuppdrag']['uppdrag'])) { + $this->processPersonUppdrag($person->intressent_id, $personInfo['personuppdrag']['uppdrag']); + } + + // Process person information + if (isset($personInfo['personuppgift']['uppgift']) && is_array($personInfo['personuppgift']['uppgift'])) { + $this->processPersonUppgift($person->intressent_id, $personInfo['personuppgift']['uppgift']); + } + } + + protected function processPersonUppdrag($intressentId, $uppdragList) + { + foreach ($uppdragList as $uppdrag) { + PersonUppdrag::updateOrCreate( + [ + 'intressent_id' => $intressentId, + 'organ_kod' => $uppdrag['organ_kod'] ?? null, + 'roll_kod' => $uppdrag['roll_kod'] ?? null, + 'ordningsnummer' => $uppdrag['ordningsnummer'] ?? null, + ], + [ + 'status' => $uppdrag['status'] ?? null, + 'typ' => $uppdrag['typ'] ?? null, + 'from' => isset($uppdrag['from']) ? $this->parseDate($uppdrag['from']) : null, + 'tom' => isset($uppdrag['tom']) ? $this->parseDate($uppdrag['tom']) : null, + 'uppgift' => $uppdrag['uppgift'] ?? null, + ] + ); + } + } + + protected function processPersonUppgift($intressentId, $uppgiftList) + { + foreach ($uppgiftList as $uppgift) { + PersonUppgift::updateOrCreate( + [ + 'intressent_id' => $intressentId, + 'uppgift_kod' => $uppgift['kod'] ?? null, + ], + [ + 'uppgift' => $uppgift['uppgift'] ?? null, + 'uppgift_typ' => $uppgift['typ'] ?? null, + ] + ); + } + } + + protected function importOrganData($organData) + { + $organs = isset($organData['organ']) ? $organData['organ'] : []; + + // Handle both single organ and array of organs + if (! is_array($organs) || (isset($organs['id']) && ! is_numeric(array_keys($organs)[0]))) { + $organs = [$organs]; + } + + foreach ($organs as $organInfo) { + Organ::updateOrCreate( + ['id' => $organInfo['id'] ?? null], + [ + 'kod' => $organInfo['kod'] ?? null, + 'namn' => $organInfo['namn'] ?? null, + 'typ' => $organInfo['typ'] ?? null, + 'status' => $organInfo['status'] ?? null, + 'sortering' => $organInfo['sortering'] ?? null, + 'namn_en' => $organInfo['namn_en'] ?? null, + 'domän' => $organInfo['doman'] ?? $organInfo['domän'] ?? null, + 'beskrivning' => $organInfo['beskrivning'] ?? null, + ] + ); + } + } + + protected function importSingleDokument($dokument) + { + $this->processDokument($dokument); + } + + protected function parseDate($dateString) + { + if (empty($dateString)) { + return null; + } + + try { + return \Carbon\Carbon::parse($dateString); + } catch (\Exception $e) { + Log::warning("Could not parse date: {$dateString}"); + + return null; + } + } +} diff --git a/app/Livewire/Motion/Search.php b/app/Livewire/Motion/Search.php new file mode 100644 index 0000000..7896ca9 --- /dev/null +++ b/app/Livewire/Motion/Search.php @@ -0,0 +1,146 @@ +searchMotions(); + } + + public function updatedQuery() + { + $this->resetPage(); + $this->searchMotions(); + } + + public function updatedParty() + { + $this->resetPage(); + $this->searchMotions(); + } + + public function updatedDateInterval() + { + $this->resetPage(); + $this->searchMotions(); + } + + public function searchMotions() + { + if (empty($this->query) && empty($this->party) && empty($this->dateInterval)) { + $this->paginator = null; + + return; + } + + $this->loading = true; + + $cacheKey = 'motions_'.md5($this->query.$this->party.$this->dateInterval.$this->getPage()); + + $result = Cache::remember($cacheKey, 60 * 5, function () { + return app(RiksdagenService::class)->searchMotions( + query: $this->query, + party: $this->party, + dateInterval: $this->dateInterval, + page: $this->getPage() + ); + }); + + $motions = $result->original->dokumentlista->dokument ?? []; + $totalResults = (int) ($result->original->dokumentlista->{'@traffar'} ?? 0); + $perPage = 20; // Default items per page from Riksdag API + $currentPage = $this->getPage(); + + $this->paginator = new LengthAwarePaginator( + items: collect($motions), + total: $totalResults, + perPage: $perPage, + currentPage: $currentPage, + options: [ + 'path' => request()->url(), + 'pageName' => 'page', + ] + ); + + $this->loading = false; + } + + #[Computed()] + public function motions() + { + return $this->paginator ? collect($this->paginator->items()) : collect(); + } + + #[Computed()] + public function totalResults() + { + return $this->paginator ? $this->paginator->total() : 0; + } + + public function clearFilters() + { + $this->query = ''; + $this->party = ''; + $this->dateInterval = ''; + $this->resetPage(); + $this->searchMotions(); + } + + #[Computed()] + public function parties() + { + return collect(PartyEnum::cases())->mapWithKeys(function ($party) { + return [$party->value => $party->label()]; + }); + } + + #[Computed()] + public function dateIntervals() + { + return [ + '2025/26' => '2025/26', + '2024/25' => '2024/25', + '2023/24' => '2023/24', + '2022/23' => '2022/23', + '2021/22' => '2021/22', + '2020/21' => '2020/21', + '2019/20' => '2019/20', + '2018/19' => '2018/19', + ]; + } + + public function render() + { + $this->searchMotions(); + + return view('livewire.motion.search', [ + 'paginatedMotions' => $this->paginator, + ])->title('Sök Motioner - Riksdagen App'); + } +} diff --git a/app/Livewire/Motion/Show.php b/app/Livewire/Motion/Show.php new file mode 100644 index 0000000..933f831 --- /dev/null +++ b/app/Livewire/Motion/Show.php @@ -0,0 +1,71 @@ +motionId = $motionId; + $this->service = app(RiksdagenService::class); + + $result = Cache::remember('motion_'.$motionId, 24 * 60 * 60, function () use ($motionId) { + return $this->service->getMotion($motionId); + }); + + $this->motion = $result->original->dokumentlista->dokument[0] ?? null; + } + + #[Computed()] + public function riksdagenUrl() + { + return 'https://www.riksdagen.se/sv/dokument-lagar/dokument/'.$this->motionId; + } + + #[Computed()] + public function authors() + { + if (! $this->motion || ! isset($this->motion->dokintressent->intressent)) { + return collect(); + } + + $intressenter = $this->motion->dokintressent->intressent; + if (! is_array($intressenter)) { + $intressenter = [$intressenter]; + } + + return collect($intressenter)->where('roll', 'undertecknare'); + } + + #[Computed()] + public function attachments() + { + if (! $this->motion || ! isset($this->motion->filbilaga->fil)) { + return collect(); + } + + $files = $this->motion->filbilaga->fil; + if (! is_array($files)) { + $files = [$files]; + } + + return collect($files); + } + + public function render() + { + return view('livewire.motion.show') + ->title(($this->motion->titel ?? 'Motion').' - Riksdagen App'); + } +} diff --git a/app/Livewire/Person/Search.php b/app/Livewire/Person/Search.php index b2ad70a..41b32c2 100644 --- a/app/Livewire/Person/Search.php +++ b/app/Livewire/Person/Search.php @@ -2,30 +2,35 @@ namespace App\Livewire\Person; -use Livewire\Component; -use App\Services\RiksdagenService; use App\Enums\Parties; +use App\Services\RiksdagenService; +use Livewire\Component; class Search extends Component { public $firstName = ''; + public $lastName = ''; + public $party = ''; + public $results = []; + public $parties = []; public function mount() { - $this->parties = collect(Parties::cases())->sortBy(fn($party) => $party->label())->toArray(); + $this->parties = collect(Parties::cases())->sortBy(fn ($party) => $party->label())->toArray(); } public function search() { $service = app(RiksdagenService::class); + $this->results = $service->searchPerson( firstName: $this->firstName, lastName: $this->lastName, - party: $this->party + party: $this->party, )->original; } diff --git a/app/Livewire/Person/Show.php b/app/Livewire/Person/Show.php index cb9213b..6452b66 100644 --- a/app/Livewire/Person/Show.php +++ b/app/Livewire/Person/Show.php @@ -2,11 +2,12 @@ namespace App\Livewire\Person; -use Livewire\Component; use App\Services\RiksdagenService; -use Livewire\Attributes\Computed; use Asantibanez\LivewireCharts\Models\PieChartModel; +use Illuminate\Support\Facades\Cache; +use Livewire\Attributes\Computed; use Livewire\Attributes\Lazy; +use Livewire\Component; #[Lazy()] class Show extends Component @@ -17,9 +18,15 @@ class Show extends Component public $votes = []; + public $motions = []; + public $votesByYear = []; - public $selectedYear; + public $motionsByYear = []; + + public $votesSelectedYear; + + public $motionsSelectedYear; public $selectedUppdragTab = 'current'; @@ -27,47 +34,87 @@ class Show extends Component public $previousUppdrag = []; + private RiksdagenService $service; + public function mount($personId) { $this->personId = $personId; - $service = app(RiksdagenService::class); - $result = $service->searchPerson(mp_id: $personId); + $this->service = app(RiksdagenService::class); + $result = Cache::remember('person_'.$personId, 24 * 60 * 60, function () use ($personId) { + return $this->service->searchPerson(mp_id: $personId); + }); $this->person = $result->original->personlista->person ?? null; $this->getPersonVotes(); + $this->getPersonMotions(); $this->groupUppdrag(); } #[Computed()] public function riksdagenUrl() { - return "https://www.riksdagen.se/sv/ledamoter-partier/ledamot/" . $this->person->tilltalsnamn . "-" . $this->person->efternamn . "_" . $this->personId; + return 'https://www.riksdagen.se/sv/ledamoter-partier/ledamot/'.$this->person->tilltalsnamn.'-'.$this->person->efternamn.'_'.$this->personId; } public function getPersonVotes() { - $service = app(RiksdagenService::class); - $result = $service->searchVotes(mp_id: $this->personId); + $result = Cache::remember('person_votes_'.$this->personId, 24 * 60 * 60, function () { + return $this->service->searchVotes(mp_id: $this->personId); + }); + $votesList = $result->original->voteringlista->votering ?? []; - // Group votes by year (from rm field like 2025/26) + // Group votes by "rm" field (e.g., 2024/25) foreach ($votesList as $vote) { - $year = explode('/', $vote->rm)[0]; // Extract year from rm like "2025/26" - if (!isset($this->votesByYear[$year])) { - $this->votesByYear[$year] = []; + $rm = $vote->rm; + if (! isset($this->votesByYear[$rm])) { + $this->votesByYear[$rm] = []; } - $this->votesByYear[$year][] = $vote; + $this->votesByYear[$rm][] = $vote; } // Set default selected year to the most recent - if (!empty($this->votesByYear)) { - $this->selectedYear = max(array_keys($this->votesByYear)); + if (! empty($this->votesByYear)) { + // Sort keys as strings (e.g., 2024/25, 2023/24, ...) + $years = array_keys($this->votesByYear); + rsort($years, SORT_STRING); + $this->votesSelectedYear = $years[0]; } } - public function selectYear($year) + public function getPersonMotions() { - $this->selectedYear = $year; + $result = Cache::remember('person_motions_'.$this->personId, 24 * 60 * 60, function () { + return $this->service->getPersonMotions(mp_id: $this->personId); + }); + $motionsList = $result->original->dokumentlista->dokument ?? []; + + // Group votes by "rm" field (e.g., 2024/25) + foreach ($motionsList as $motion) { + $rm = $motion->rm; + if (! isset($this->motionsByYear[$rm])) { + $this->motionsByYear[$rm] = []; + } + $this->motionsByYear[$rm][] = $motion; + } + + // Set default selected year to the most recent + if (! empty($this->motionsByYear)) { + // Sort keys as strings (e.g., 2024/25, 2023/24, ...) + $years = array_keys($this->motionsByYear); + rsort($years, SORT_STRING); + $this->motionsSelectedYear = $years[0]; + } + } + + public function selectVotesYear($year) + { + $this->votesSelectedYear = $year; + } + + public function selectMotionsYear($year) + { + $this->motionsSelectedYear = $year; } public function selectUppdragTab($tab) @@ -77,7 +124,7 @@ class Show extends Component public function groupUppdrag() { - if (!$this->person || !isset($this->person->personuppdrag->uppdrag)) { + if (! $this->person || ! isset($this->person->personuppdrag->uppdrag)) { return; } @@ -98,11 +145,11 @@ class Show extends Component } // Sort by date (most recent first) - usort($this->currentUppdrag, function($a, $b) { + usort($this->currentUppdrag, function ($a, $b) { return \Carbon\Carbon::parse($b->from)->timestamp - \Carbon\Carbon::parse($a->from)->timestamp; }); - usort($this->previousUppdrag, function($a, $b) { + usort($this->previousUppdrag, function ($a, $b) { return \Carbon\Carbon::parse($b->tom ?: $b->from)->timestamp - \Carbon\Carbon::parse($a->tom ?: $a->from)->timestamp; }); } @@ -110,16 +157,16 @@ class Show extends Component #[Computed()] public function votingStatistics() { - if (!$this->selectedYear || !isset($this->votesByYear[$this->selectedYear])) { + if (! $this->votesSelectedYear || ! isset($this->votesByYear[$this->votesSelectedYear])) { return []; } - $votes = $this->votesByYear[$this->selectedYear]; + $votes = $this->votesByYear[$this->votesSelectedYear]; $statistics = []; foreach ($votes as $vote) { $voteType = $vote->rost; - if (!isset($statistics[$voteType])) { + if (! isset($statistics[$voteType])) { $statistics[$voteType] = 0; } $statistics[$voteType]++; @@ -133,8 +180,8 @@ class Show extends Component { $statistics = $this->votingStatistics; - $pieChart = (new PieChartModel()) - ->setTitle('Voteringsstatistik för ' . $this->selectedYear) + $pieChart = (new PieChartModel) + ->setTitle('Voteringsstatistik för '.$this->votesSelectedYear) ->setAnimated(true) ->withDataLabels(); diff --git a/app/Models/Anforande.php b/app/Models/Anforande.php new file mode 100644 index 0000000..6e108a3 --- /dev/null +++ b/app/Models/Anforande.php @@ -0,0 +1,20 @@ +belongsTo(Dokument::class, 'dok_hangar_id', 'hangar_id'); + } + + public function person() + { + return $this->belongsTo(Person::class, 'intressent_id', 'intressent_id'); + } +} diff --git a/app/Models/Debatt.php b/app/Models/Debatt.php new file mode 100644 index 0000000..e5d780a --- /dev/null +++ b/app/Models/Debatt.php @@ -0,0 +1,20 @@ +belongsTo(Dokument::class, 'hangar_id', 'hangar_id'); + } + + public function person() + { + return $this->belongsTo(Person::class, 'intressent_id', 'intressent_id'); + } +} diff --git a/app/Models/DokAktivitet.php b/app/Models/DokAktivitet.php new file mode 100644 index 0000000..446b215 --- /dev/null +++ b/app/Models/DokAktivitet.php @@ -0,0 +1,27 @@ +belongsTo(Dokument::class, 'hangar_id', 'hangar_id'); + } +} diff --git a/app/Models/DokBilaga.php b/app/Models/DokBilaga.php new file mode 100644 index 0000000..999d395 --- /dev/null +++ b/app/Models/DokBilaga.php @@ -0,0 +1,28 @@ +belongsTo(Dokument::class, 'hangar_id', 'hangar_id'); + } +} diff --git a/app/Models/DokForslag.php b/app/Models/DokForslag.php new file mode 100644 index 0000000..dec4d30 --- /dev/null +++ b/app/Models/DokForslag.php @@ -0,0 +1,34 @@ +belongsTo(Dokument::class, 'hangar_id', 'hangar_id'); + } +} diff --git a/app/Models/DokIntressent.php b/app/Models/DokIntressent.php new file mode 100644 index 0000000..aabcb61 --- /dev/null +++ b/app/Models/DokIntressent.php @@ -0,0 +1,31 @@ +belongsTo(Dokument::class, 'hangar_id', 'hangar_id'); + } + + public function person() + { + return $this->belongsTo(Person::class, 'intressent_id', 'intressent_id'); + } +} diff --git a/app/Models/DokMotforslag.php b/app/Models/DokMotforslag.php new file mode 100644 index 0000000..28f0b57 --- /dev/null +++ b/app/Models/DokMotforslag.php @@ -0,0 +1,15 @@ +belongsTo(Dokument::class, 'hangar_id', 'hangar_id'); + } +} diff --git a/app/Models/DokReferens.php b/app/Models/DokReferens.php new file mode 100644 index 0000000..a8ee5e6 --- /dev/null +++ b/app/Models/DokReferens.php @@ -0,0 +1,31 @@ +belongsTo(Dokument::class, 'hangar_id', 'hangar_id'); + } +} diff --git a/app/Models/DokUppgift.php b/app/Models/DokUppgift.php new file mode 100644 index 0000000..f130b30 --- /dev/null +++ b/app/Models/DokUppgift.php @@ -0,0 +1,30 @@ + 'datetime', + ]; + + public function dokument() + { + return $this->belongsTo(Dokument::class, 'hangar_id', 'hangar_id'); + } +} diff --git a/app/Models/DokUtskottsforslag.php b/app/Models/DokUtskottsforslag.php new file mode 100644 index 0000000..acbd75f --- /dev/null +++ b/app/Models/DokUtskottsforslag.php @@ -0,0 +1,15 @@ +belongsTo(Dokument::class, 'hangar_id', 'hangar_id'); + } +} diff --git a/app/Models/Dokument.php b/app/Models/Dokument.php new file mode 100644 index 0000000..2119f0b --- /dev/null +++ b/app/Models/Dokument.php @@ -0,0 +1,102 @@ +hasMany(DokUtskottsforslag::class, 'hangar_id', 'hangar_id'); + } + + public function motforslag() + { + return $this->hasMany(DokMotforslag::class, 'hangar_id', 'hangar_id'); + } + + public function aktiviteter() + { + return $this->hasMany(DokAktivitet::class, 'hangar_id', 'hangar_id'); + } + + public function intressenter() + { + return $this->hasMany(DokIntressent::class, 'hangar_id', 'hangar_id'); + } + + public function forslag() + { + return $this->hasMany(DokForslag::class, 'hangar_id', 'hangar_id'); + } + + public function uppgifter() + { + return $this->hasMany(DokUppgift::class, 'hangar_id', 'hangar_id'); + } + + public function bilagor() + { + return $this->hasMany(DokBilaga::class, 'hangar_id', 'hangar_id'); + } + + public function referenser() + { + return $this->hasMany(DokReferens::class, 'hangar_id', 'hangar_id'); + } + + public function debatter() + { + return $this->hasMany(Debatt::class, 'hangar_id', 'hangar_id'); + } + + public function voteringar() + { + return $this->hasMany(Votering::class, 'hangar_id', 'hangar_id'); + } + + public function anforanden() + { + return $this->hasMany(Anforande::class, 'dok_hangar_id', 'hangar_id'); + } +} diff --git a/app/Models/Organ.php b/app/Models/Organ.php new file mode 100644 index 0000000..fe4032a --- /dev/null +++ b/app/Models/Organ.php @@ -0,0 +1,31 @@ +hasMany(PersonUppdrag::class, 'organ_kod', 'kod'); + } +} diff --git a/app/Models/Person.php b/app/Models/Person.php new file mode 100644 index 0000000..85d8b75 --- /dev/null +++ b/app/Models/Person.php @@ -0,0 +1,61 @@ +hasMany(PersonUppdrag::class, 'intressent_id', 'intressent_id'); + } + + public function uppgifter() + { + return $this->hasMany(PersonUppgift::class, 'intressent_id', 'intressent_id'); + } + + public function voteringar() + { + return $this->hasMany(Votering::class, 'intressent_id', 'intressent_id'); + } + + public function anforanden() + { + return $this->hasMany(Anforande::class, 'intressent_id', 'intressent_id'); + } + + public function debatter() + { + return $this->hasMany(Debatt::class, 'intressent_id', 'intressent_id'); + } + + public function dokIntressenter() + { + return $this->hasMany(DokIntressent::class, 'intressent_id', 'intressent_id'); + } +} diff --git a/app/Models/PersonUppdrag.php b/app/Models/PersonUppdrag.php new file mode 100644 index 0000000..95807a3 --- /dev/null +++ b/app/Models/PersonUppdrag.php @@ -0,0 +1,44 @@ + 'datetime', + 'tom' => 'datetime', + ]; + + public function person() + { + return $this->belongsTo(Person::class, 'intressent_id', 'intressent_id'); + } + + public function organ() + { + return $this->belongsTo(Organ::class, 'organ_kod', 'kod'); + } + + public function roll() + { + return $this->belongsTo(Roll::class, 'roll_kod', 'kod'); + } +} diff --git a/app/Models/PersonUppgift.php b/app/Models/PersonUppgift.php new file mode 100644 index 0000000..5862b90 --- /dev/null +++ b/app/Models/PersonUppgift.php @@ -0,0 +1,24 @@ +belongsTo(Person::class, 'intressent_id', 'intressent_id'); + } +} diff --git a/app/Models/Planering.php b/app/Models/Planering.php new file mode 100644 index 0000000..c5f1070 --- /dev/null +++ b/app/Models/Planering.php @@ -0,0 +1,31 @@ + 'datetime', + 'publicerad' => 'datetime', + 'uppdaterad' => 'datetime', + 'wn_expires' => 'datetime', + 'timestamp' => 'datetime', + 'slutdatum' => 'datetime', + 'webbtvlive' => 'boolean', + ]; + + public function person() + { + return $this->belongsTo(Person::class, 'intressent_id', 'intressent_id'); + } + + public function mottagare() + { + return $this->belongsTo(Person::class, 'mottagare_id', 'intressent_id'); + } +} diff --git a/app/Models/Riksmote.php b/app/Models/Riksmote.php new file mode 100644 index 0000000..571599c --- /dev/null +++ b/app/Models/Riksmote.php @@ -0,0 +1,16 @@ + 'datetime', + 'slut' => 'datetime', + ]; +} diff --git a/app/Models/Roll.php b/app/Models/Roll.php new file mode 100644 index 0000000..7f76feb --- /dev/null +++ b/app/Models/Roll.php @@ -0,0 +1,16 @@ +hasMany(PersonUppdrag::class, 'roll_kod', 'kod'); + } +} diff --git a/app/Models/Votering.php b/app/Models/Votering.php new file mode 100644 index 0000000..49636f6 --- /dev/null +++ b/app/Models/Votering.php @@ -0,0 +1,20 @@ +belongsTo(Dokument::class, 'hangar_id', 'hangar_id'); + } + + public function person() + { + return $this->belongsTo(Person::class, 'intressent_id', 'intressent_id'); + } +} diff --git a/app/Services/RiksdagenService.php b/app/Services/RiksdagenService.php index 466a7bc..22aab93 100644 --- a/app/Services/RiksdagenService.php +++ b/app/Services/RiksdagenService.php @@ -22,7 +22,7 @@ class RiksdagenService string $lastName = '', string $party = '' ): JsonResponse { - $response = $this->http->get('personlista/', [ + $response = $this->http->get('personlista/', [ 'query' => [ 'iid' => $mp_id, 'parti' => $party, @@ -30,8 +30,8 @@ class RiksdagenService 'enamn' => $lastName, 'utformat' => 'json', 'sort' => 'sorteringsnamn', - 'sortorder' => 'asc' - ] + 'sortorder' => 'asc', + ], ])->getBody()->getContents(); $data = json_decode($response); @@ -44,14 +44,92 @@ class RiksdagenService string $party = '', string $date = '', ): JsonResponse { - $response = $this->http->get('voteringlista/', [ + $response = $this->http->get('voteringlista/', [ 'query' => [ 'iid' => $mp_id, 'rm' => $date, 'parti' => $party, 'utformat' => 'json', - 'sz' => '500', - ] + 'sz' => '3000', + ], + ])->getBody()->getContents(); + + $data = json_decode($response); + + return response()->json($data); + } + + public function getPersonMotions( + string $mp_id, + string $date = '', + ): JsonResponse { + $response = $this->http->get('dokumentlista/', [ + 'query' => [ + 'iid' => $mp_id, + 'rm' => $date, + 'doktyp' => 'mot', + 'utformat' => 'json', + ], + ])->getBody()->getContents(); + + $data = json_decode($response); + + return response()->json($data); + } + + public function getPartyMotions( + string $party, + string $date = '', + + ): JsonResponse { + $response = $this->http->get('dokumentlista/', [ + 'query' => [ + 'parti' => $party, + 'rm' => $date, + 'doktyp' => 'mot', + 'utformat' => 'json', + ], + ])->getBody()->getContents(); + + $data = json_decode($response); + + return response()->json($data); + } + + public function searchMotions( + string $query = '', + string $party = '', + string $dateInterval = '', + int $page = 1 + ): JsonResponse { + $response = $this->http->get('dokumentlista/', [ + 'query' => array_filter([ + 'sok' => $query, + 'parti' => $party, + 'rm' => $dateInterval, + 'doktyp' => 'mot', + 'utformat' => 'json', + 'p' => $page, + ]), + ])->getBody()->getContents(); + + $data = json_decode($response); + + return response()->json($data); + } + + public function getMotion( + string $bet, + string $tempbet, + string $nr, + ): JsonResponse { + $response = $this->http->get('dokumentlista/', [ + 'query' => [ + 'bet' => $bet, + 'tempbet' => $tempbet, + 'nr' => $nr, + 'utformat' => 'json', + ], ])->getBody()->getContents(); $data = json_decode($response); diff --git a/database/migrations/2025_12_27_000000_create_riksdagen_tables.php b/database/migrations/2025_12_27_000000_create_riksdagen_tables.php new file mode 100644 index 0000000..a8fe563 --- /dev/null +++ b/database/migrations/2025_12_27_000000_create_riksdagen_tables.php @@ -0,0 +1,340 @@ +integer('hangar_id'); + $table->string('dok_id'); + $table->string('rm'); + $table->string('beteckning')->nullable(); + $table->string('typ')->nullable(); + $table->string('subtyp')->nullable(); + $table->string('doktyp')->nullable(); + $table->string('typrubrik')->nullable(); + $table->string('dokumentnamn')->nullable(); + $table->string('debattnamn')->nullable(); + $table->string('tempbeteckning')->nullable(); + $table->string('organ')->nullable(); + $table->string('mottagare')->nullable(); + $table->integer('nummer')->nullable(); + $table->integer('slutnummer')->nullable(); + $table->dateTime('datum')->nullable(); + $table->dateTime('systemdatum')->nullable(); + $table->dateTime('publicerad')->nullable(); + $table->string('titel')->nullable(); + $table->string('subtitel')->nullable(); + $table->string('status')->nullable(); + $table->string('htmlformat')->nullable(); + $table->string('relaterat_id')->nullable(); + $table->string('source')->nullable(); + $table->string('sourceid')->nullable(); + $table->string('dokument_url_text')->nullable(); + $table->string('dokument_url_html')->nullable(); + $table->string('dokumentstatus_url_xml')->nullable(); + $table->string('utskottsforslag_url_xml')->nullable(); + $table->text('html')->nullable(); + }); + // dokutskottsforslag + Schema::create('dokutskottsforslag', function (Blueprint $table) { + $table->integer('hangar_id'); + $table->integer('punkt'); + $table->string('beteckning', 50)->nullable(); + $table->string('rubrik')->nullable(); + $table->string('forslag')->nullable(); + $table->text('forslag_del2')->nullable(); + $table->string('beslutstyp')->nullable(); + $table->string('beslut', 100)->nullable(); + $table->integer('motforslag_nummer')->nullable(); + $table->string('motforslag_partier')->nullable(); + $table->string('votering_id')->nullable(); + $table->string('votering_sammanfattning_html')->nullable(); + $table->string('votering_ledamot_url_xml')->nullable(); + $table->string('rm')->nullable(); + $table->string('bet')->nullable(); + $table->string('vinnare')->nullable(); + }); + // dokmotforslag + Schema::create('dokmotforslag', function (Blueprint $table) { + $table->integer('hangar_id'); + $table->integer('nummer'); + $table->string('rubrik')->nullable(); + $table->text('forslag')->nullable(); + $table->string('partier')->nullable(); + $table->string('typ')->nullable(); + $table->integer('utskottsforslag_punkt')->nullable(); + $table->string('id', 50)->nullable(); + }); + // dokaktivitet + Schema::create('dokaktivitet', function (Blueprint $table) { + $table->integer('hangar_id'); + $table->string('kod')->nullable(); + $table->string('namn')->nullable(); + $table->dateTime('datum')->nullable(); + $table->string('status')->nullable(); + $table->string('ordning')->nullable(); + $table->string('process')->nullable(); + }); + // dokintressent + Schema::create('dokintressent', function (Blueprint $table) { + $table->integer('hangar_id'); + $table->string('intressent_id'); + $table->string('namn')->nullable(); + $table->string('partibet')->nullable(); + $table->integer('ordning')->nullable(); + $table->string('roll')->nullable(); + }); + // dokforslag + Schema::create('dokforslag', function (Blueprint $table) { + $table->integer('hangar_id'); + $table->integer('nummer')->nullable(); + $table->string('beteckning')->nullable(); + $table->string('lydelse', 500)->nullable(); + $table->string('lydelse2')->nullable(); + $table->string('utskottet')->nullable(); + $table->string('kammaren')->nullable(); + $table->string('behandlas_i')->nullable(); + $table->string('behandlas_i_punkt')->nullable(); + $table->string('kammarbeslutstyp')->nullable(); + $table->string('intressent')->nullable(); + $table->string('avsnitt')->nullable(); + $table->string('grundforfattning')->nullable(); + $table->string('andringsforfattning')->nullable(); + }); + // dokuppgift + Schema::create('dokuppgift', function (Blueprint $table) { + $table->integer('hangar_id'); + $table->string('kod')->nullable(); + $table->string('namn')->nullable(); + $table->text('text')->nullable(); + $table->string('dok_id')->nullable(); + $table->dateTime('systemdatum')->nullable(); + }); + // dokbilaga + Schema::create('dokbilaga', function (Blueprint $table) { + $table->integer('hangar_id'); + $table->string('dok_id')->nullable(); + $table->string('titel')->nullable(); + $table->string('subtitel')->nullable(); + $table->string('filnamn')->nullable(); + $table->integer('filstorlek')->nullable(); + $table->string('filtyp')->nullable(); + $table->string('fil_url')->nullable(); + }); + // dokreferens + Schema::create('dokreferens', function (Blueprint $table) { + $table->integer('hangar_id'); + $table->string('referenstyp')->nullable(); + $table->string('uppgift')->nullable(); + $table->string('ref_dok_id')->nullable(); + $table->string('ref_dok_typ')->nullable(); + $table->string('ref_dok_rm')->nullable(); + $table->string('ref_dok_bet')->nullable(); + $table->string('ref_dok_titel')->nullable(); + $table->string('ref_dok_subtitel')->nullable(); + $table->string('ref_dok_subtyp')->nullable(); + $table->string('ref_dok_dokumentnamn')->nullable(); + }); + // debatt + Schema::create('debatt', function (Blueprint $table) { + $table->integer('hangar_id'); + $table->string('video_id')->nullable(); + $table->string('video_url')->nullable(); + $table->integer('startpos')->nullable(); + $table->string('tumnagel')->nullable(); + $table->string('tumnagel_stor')->nullable(); + $table->string('anf_video_id')->nullable(); + $table->integer('anf_hangar_id')->nullable(); + $table->integer('anf_sekunder')->nullable(); + $table->string('anf_klockslag')->nullable(); + $table->dateTime('datumtid')->nullable(); + $table->dateTime('anf_datum')->nullable(); + $table->string('anf_typ')->nullable(); + $table->string('anf_text')->nullable(); + $table->string('anf_beteckning')->nullable(); + $table->string('anf_nummer')->nullable(); + $table->string('talare')->nullable(); + $table->string('intressent_id')->nullable(); + $table->string('parti')->nullable(); + $table->string('anf_rm')->nullable(); + }); + // votering + Schema::create('votering', function (Blueprint $table) { + $table->string('rm'); + $table->string('beteckning')->nullable(); + $table->integer('hangar_id'); + $table->string('votering_id')->nullable(); + $table->integer('punkt')->nullable(); + $table->string('namn')->nullable(); + $table->string('intressent_id')->nullable(); + $table->string('parti')->nullable(); + $table->string('valkrets')->nullable(); + $table->integer('valkretsnummer')->nullable(); + $table->string('iort')->nullable(); + $table->string('rost')->nullable(); + $table->string('avser')->nullable(); + $table->string('votering')->nullable(); + $table->integer('banknummer')->nullable(); + $table->string('fornamn')->nullable(); + $table->string('efternamn')->nullable(); + $table->string('kon')->nullable(); + $table->integer('fodd')->nullable(); + $table->dateTime('datum')->nullable(); + }); + // anforande + Schema::create('anforande', function (Blueprint $table) { + $table->integer('dok_hangar_id'); + $table->string('dok_id', 50); + $table->string('dok_titel')->nullable(); + $table->string('dok_rm', 20)->nullable(); + $table->integer('dok_nummer')->nullable(); + $table->dateTime('dok_datum')->nullable(); + $table->string('avsnittsrubrik')->nullable(); + $table->string('underrubrik')->nullable(); + $table->string('kammaraktivitet', 250)->nullable(); + $table->string('anforande_id', 50); + $table->integer('anforande_nummer')->nullable(); + $table->string('talare', 250)->nullable(); + $table->string('parti', 50)->nullable(); + $table->text('anforandetext')->nullable(); + $table->string('intressent_id', 50)->nullable(); + $table->string('rel_dok_id', 50)->nullable(); + $table->char('replik', 1)->nullable(); + $table->dateTime('systemdatum')->nullable(); + }); + // person + Schema::create('person', function (Blueprint $table) { + $table->string('intressent_id', 20)->primary(); + $table->smallInteger('född_år')->nullable(); + $table->string('kön', 6)->nullable(); + $table->string('efternamn', 50)->nullable(); + $table->string('tilltalsnamn', 50)->nullable(); + $table->string('sorteringsnamn', 80)->nullable(); + $table->string('iort', 40)->nullable(); + $table->string('parti', 40)->nullable(); + $table->string('valkrets', 50)->nullable(); + $table->string('status', 100)->nullable(); + }); + // personuppdrag + Schema::create('personuppdrag', function (Blueprint $table) { + $table->string('organ_kod', 20); + $table->string('roll_kod', 40); + $table->integer('ordningsnummer'); + $table->string('status', 20)->nullable(); + $table->string('typ', 20)->nullable(); + $table->dateTime('from')->nullable(); + $table->dateTime('tom')->nullable(); + $table->string('uppgift', 500)->nullable(); + $table->string('intressent_id', 50); + }); + // personuppgift + Schema::create('personuppgift', function (Blueprint $table) { + $table->string('uppgift_kod', 50); + $table->text('uppgift')->nullable(); + $table->string('uppgift_typ', 50)->nullable(); + $table->string('intressent_id', 50); + }); + // planering + Schema::create('planering', function (Blueprint $table) { + $table->integer('nyckel'); + $table->string('id', 50); + $table->string('rm', 12)->nullable(); + $table->string('typ', 40)->nullable(); + $table->char('dokserie_id', 2)->nullable(); + $table->string('subtyp', 40)->nullable(); + $table->string('bet', 40)->nullable(); + $table->string('tempbet', 40)->nullable(); + $table->string('intressent', 80)->nullable(); + $table->integer('nummer')->nullable(); + $table->integer('slutnummer')->nullable(); + $table->dateTime('datum')->nullable(); + $table->dateTime('publicerad')->nullable(); + $table->string('status', 40)->nullable(); + $table->string('titel', 300)->nullable(); + $table->string('subtitel', 255)->nullable(); + $table->text('html')->nullable(); + $table->text('toc')->nullable(); + $table->string('refcss', 66)->nullable(); + $table->string('url', 100)->nullable(); + $table->dateTime('uppdaterad')->nullable(); + $table->integer('storlek')->nullable(); + $table->string('source', 20)->nullable(); + $table->dateTime('wn_expires')->nullable(); + $table->string('wn_cachekey', 50)->nullable(); + $table->string('wn_status', 20)->nullable(); + $table->string('wn_checksum', 40)->nullable(); + $table->integer('wn_nid')->nullable(); + $table->string('wn_RawUrl', 255)->nullable(); + $table->string('wn_SourceID', 80)->nullable(); + $table->dateTime('timestamp')->nullable(); + $table->string('rel_id', 50)->nullable(); + $table->string('klockslag', 10)->nullable(); + $table->string('grupp', 20)->nullable(); + $table->string('format', 20)->nullable(); + $table->string('intressent_id', 13)->nullable(); + $table->string('mottagare_id', 13)->nullable(); + $table->string('mottagare', 80)->nullable(); + $table->integer('hangar_id')->nullable(); + $table->string('plats', 150)->nullable(); + $table->dateTime('slutdatum')->nullable(); + $table->tinyInteger('webbtvlive')->nullable(); + }); + // organ + Schema::create('organ', function (Blueprint $table) { + $table->integer('id')->primary(); + $table->string('kod', 50)->nullable(); + $table->string('namn', 100)->nullable(); + $table->string('typ', 50)->nullable(); + $table->string('status', 12)->nullable(); + $table->integer('sortering')->nullable(); + $table->string('namn_en', 100)->nullable(); + $table->string('domän', 50)->nullable(); + $table->string('beskrivning', 1000)->nullable(); + }); + // roll + Schema::create('roll', function (Blueprint $table) { + $table->integer('pk')->primary(); + $table->string('kod', 50)->nullable(); + $table->string('namn', 100)->nullable(); + $table->integer('sort')->nullable(); + }); + // riksmote + Schema::create('riksmote', function (Blueprint $table) { + $table->integer('pk')->primary(); + $table->string('riksmote', 20)->nullable(); + $table->string('id', 3)->nullable(); + $table->dateTime('start')->nullable(); + $table->dateTime('slut')->nullable(); + $table->string('mandatperiod', 20)->nullable(); + }); + } + + public function down() + { + Schema::dropIfExists('dokument'); + Schema::dropIfExists('dokutskottsforslag'); + Schema::dropIfExists('dokmotforslag'); + Schema::dropIfExists('dokaktivitet'); + Schema::dropIfExists('dokintressent'); + Schema::dropIfExists('dokforslag'); + Schema::dropIfExists('dokuppgift'); + Schema::dropIfExists('dokbilaga'); + Schema::dropIfExists('dokreferens'); + Schema::dropIfExists('debatt'); + Schema::dropIfExists('votering'); + Schema::dropIfExists('anforande'); + Schema::dropIfExists('person'); + Schema::dropIfExists('personuppdrag'); + Schema::dropIfExists('personuppgift'); + Schema::dropIfExists('planering'); + Schema::dropIfExists('organ'); + Schema::dropIfExists('roll'); + Schema::dropIfExists('riksmote'); + } +}; diff --git a/package-lock.json b/package-lock.json index 8dbff16..31e5c37 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,9 @@ "tailwindcss": "^4.0.7", "vite": "^7.0.4" }, + "devDependencies": { + "prettier-plugin-blade": "^2.1.21" + }, "optionalDependencies": { "@rollup/rollup-linux-x64-gnu": "4.9.5", "@tailwindcss/oxide-linux-x64-gnu": "^4.0.1", @@ -2056,6 +2059,36 @@ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", "license": "MIT" }, + "node_modules/prettier": { + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.7.4.tgz", + "integrity": "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==", + "dev": true, + "license": "MIT", + "peer": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-plugin-blade": { + "version": "2.1.21", + "resolved": "https://registry.npmjs.org/prettier-plugin-blade/-/prettier-plugin-blade-2.1.21.tgz", + "integrity": "sha512-+BPBPvla/Ppr0MVrqMAO+FTwxpXUYo8zhQPIGC7psNuMbB24y84cGrJ4Uc02GHTQN0q8txeG4Y4MxyJWgOujyQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "prettier": ">=3" + } + }, "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", diff --git a/package.json b/package.json index 688bea8..9f855a4 100644 --- a/package.json +++ b/package.json @@ -19,5 +19,8 @@ "@rollup/rollup-linux-x64-gnu": "4.9.5", "@tailwindcss/oxide-linux-x64-gnu": "^4.0.1", "lightningcss-linux-x64-gnu": "^1.29.1" + }, + "devDependencies": { + "prettier-plugin-blade": "^2.1.21" } } diff --git a/resources/views/livewire/home-page.blade.php b/resources/views/livewire/home-page.blade.php index f12b767..2a646ed 100644 --- a/resources/views/livewire/home-page.blade.php +++ b/resources/views/livewire/home-page.blade.php @@ -6,30 +6,117 @@ Riksdagen App

- Utforska information om riksdagsledamöter, deras röster och uppdrag i Sveriges riksdag + Utforska information om riksdagsledamöter, deras röster och + uppdrag i Sveriges riksdag

-
- +
-
+
-
- - +
+ +
-

Ledamöter

+

+ Ledamöter +

- Sök och utforska information om riksdagsledamöter, deras rösthistorik och uppdrag + Sök och utforska information om riksdagsledamöter, + deras rösthistorik och uppdrag

-
- Sök ledamöter - - +
+ + Sök ledamöter + + + + +
+
+
+
+ + + +
+
+
+ + + +
+

+ Motioner +

+

+ Sök bland motioner som lämnats till riksdagen av + ledamöterna +

+
+ + Sök motioner + + +
@@ -37,16 +124,33 @@
-
+
-
- - +
+ +
-

Propositioner

+

+ Propositioner +

- Sök och läs propositioner och andra dokument från riksdagen + Sök och läs propositioner och andra dokument från + riksdagen

Kommer snart @@ -55,14 +159,30 @@
-
+
-
- - +
+ +
-

Statistik

+

+ Statistik +

Se röststatistik och analyser för partier och ledamöter

@@ -72,33 +192,31 @@
- -
-
-
- - - -
-

Kalender

-

- Se kommande voteringar och riksdagsmöten -

-
- Kommer snart -
-
-
- -
+
-
- - +
+ +
-

Partier

+

+ Partier +

Utforska partier och deras ståndpunkter i olika frågor

@@ -109,14 +227,30 @@
-
+
-
- - +
+ +
-

Sök Allmänt

+

+ Sök Allmänt +

Sök i alla riksdagens dokument och voteringar

@@ -125,7 +259,6 @@
-
diff --git a/resources/views/livewire/motion/search.blade.php b/resources/views/livewire/motion/search.blade.php new file mode 100644 index 0000000..647e18f --- /dev/null +++ b/resources/views/livewire/motion/search.blade.php @@ -0,0 +1,361 @@ +
+
+ + + + +
+

Sök Motioner

+

+ Sök bland alla motioner som lämnats till riksdagen +

+
+ + +
+
+ +
+ + +
+ + +
+ + +
+ + +
+ + +
+
+ + +
+ +
+
+ + + + + + @if (! $loading) + @if ($this->motions->isNotEmpty()) + +
+
+

+ Hittade {{ number_format($this->totalResults) }} + motioner +

+
+ Sida {{ $this->getPage() }} av + {{ $paginatedMotions->lastPage() }} +
+
+
+ + +
+ @foreach ($paginatedMotions as $motion) +
+
+
+
+
+

+ + {{ $motion->titel }} + +

+ @if (! empty($motion->undertitel)) +

+ {{ $motion->undertitel }} +

+ @endif +
+ + {{ $motion->subtyp }} + +
+ +
+
+ + Beteckning + +

+ {{ $motion->rm }}:{{ $motion->beteckning }} +

+
+
+ + Datum + +

+ {{ \Carbon\Carbon::parse($motion->datum)->format('Y-m-d') }} +

+
+
+ + Status + + + {{ $motion->status }} + +
+
+ + Utskott + +

+ {{ $motion->organ ?: '-' }} +

+
+
+ + @if (! empty($motion->summary)) +

+ {!! strip_tags($motion->summary, '') !!} +

+ @endif +
+ + +
+ + Visa detaljer + + @if (! empty($motion->dokument_url_html)) + + Läs motion + + @endif +
+
+
+ @endforeach +
+ + +
+ @if ($paginatedMotions) + {{ $paginatedMotions->links() }} + @endif +
+ @elseif (! empty($query) || ! empty($party) || ! empty($dateInterval)) + +
+ + + +

+ Inga motioner hittades +

+

+ Försök ändra dina sökkriterier. +

+ +
+ @else + +
+ + + +

+ Börja din sökning +

+

+ Använd sökfälten ovan för att hitta motioner. +

+
+ @endif + @endif +
+
diff --git a/resources/views/livewire/motion/show.blade.php b/resources/views/livewire/motion/show.blade.php new file mode 100644 index 0000000..7563432 --- /dev/null +++ b/resources/views/livewire/motion/show.blade.php @@ -0,0 +1,486 @@ +
+
+ + + + @if ($motion) + +
+
+
+
+
+

+ {{ $motion->titel }} +

+ @if (! empty($motion->undertitel)) +

+ {{ $motion->undertitel }} +

+ @endif +
+ + {{ $motion->subtyp }} + +
+ + +
+
+
+ Beteckning +
+
+ {{ $motion->rm }}:{{ $motion->beteckning }} +
+
+
+
+ Datum +
+
+ {{ \Carbon\Carbon::parse($motion->datum)->format('Y-m-d') }} +
+
+
+
+ Status +
+ + {{ $motion->status }} + +
+
+
+ Utskott +
+
+ {{ $motion->organ ?: 'Ej tilldelat' }} +
+
+
+ + +
+ @if (! empty($motion->dokument_url_html)) + + + + + Läs fullständig motion + + @endif + + + + + + Visa på riksdag.se + +
+
+
+
+ + + @if (! empty($motion->summary)) +
+
+
+ + + +
+

+ Sammanfattning +

+
+
+ {!! strip_tags($motion->summary, '') !!} +
+
+ @endif + + + @if ($this->authors->isNotEmpty()) +
+
+
+ + + +
+

+ Motionärer ({{ $this->authors->count() }}) +

+
+
+ @foreach ($this->authors as $author) +
+
+
+
+ {{ $author->namn }} +
+
+ {{ $author->partibet }} +
+
+ + Visa profil + +
+
+ @endforeach +
+
+ @endif + + + @if ($this->attachments->isNotEmpty()) +
+
+
+ + + +
+

Bilagor

+
+
+ @foreach ($this->attachments as $file) +
+
+
+ @if ($file->typ == 'pdf') + + + + @elseif ($file->typ == 'docx') + + + + @else + + + + @endif +
+
+
+ {{ $file->namn }} +
+
+ {{ strtoupper($file->typ) }} • + {{ number_format($file->storlek / 1024, 1) }} + KB +
+
+
+ + Ladda ner + +
+ @endforeach +
+
+ @endif + + +
+
+
+ + + +
+

+ Teknisk information +

+
+
+
+
+ Dokument-ID +
+
+ {{ $motion->dok_id }} +
+
+
+
+ Publicerad +
+
+ {{ \Carbon\Carbon::parse($motion->publicerad)->format('Y-m-d') }} +
+
+
+
+ Systemdatum +
+
+ {{ \Carbon\Carbon::parse($motion->systemdatum)->format('Y-m-d H:i') }} +
+
+
+
+ Källa +
+
{{ $motion->kalla }}
+
+
+
+ @else + +
+ + + +

+ Motion inte hittad +

+

+ Motionen med ID {{ $motionId }} kunde inte hittas. +

+ + Tillbaka till sökning + +
+ @endif +
+
diff --git a/resources/views/livewire/person/search.blade.php b/resources/views/livewire/person/search.blade.php index 7ed3108..4b65f2e 100644 --- a/resources/views/livewire/person/search.blade.php +++ b/resources/views/livewire/person/search.blade.php @@ -88,43 +88,46 @@ @foreach ($persons as $person) + @elseif ($results && (!isset($results->personlista->person) || empty($results->personlista->person))) +
+
+ + + + Inga ledamöter hittades med de angivna + sökkriterier. +
+
+ @endif
- @elseif ($results && (!isset($results->personlista->person) || empty($results->personlista->person))) -
-
- - - - Inga ledamöter hittades med de angivna sökkriterier. -
-
- @endif
-
diff --git a/resources/views/livewire/person/show.blade.php b/resources/views/livewire/person/show.blade.php index 9c1124f..100c108 100644 --- a/resources/views/livewire/person/show.blade.php +++ b/resources/views/livewire/person/show.blade.php @@ -2,306 +2,330 @@
@if ($person) -
+
- {{ $person->tilltalsnamn }} + {{ $person->tilltalsnamn }} + + Foto: Sveriges Riksdag +
-

{{ $person->tilltalsnamn }} - {{ $person->efternamn }} ({{ $person->parti }})

+

+ {{ $person->tilltalsnamn }} + {{ $person->efternamn }} + ({{ $person->parti }}) +

+ class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-blue-100 text-blue-800 grap-2" + > {{ App\Enums\Parties::from($person->parti)->label() }} - + + + + {{ $person->valkrets }} valkrets - {{ $person->valkrets }} valkrets
-
Född
-
{{ $person->fodd_ar }}
+
+ Född +
+
+ {{ $person->fodd_ar }} + ({{ Carbon\Carbon::parse($person->fodd_ar)->age }} + år) +
-
Kön
-
{{ ucfirst($person->kon) }}
+
+ Kön +
+
+ {{ ucfirst($person->kon) }} +
-
Status
-
{{ $person->status }}
+
+ Status +
+
+ {{ $person->status }} +
-
E-post
+
+ E-post +
{{ str_replace('[på]', '@', collect($person->personuppgift->uppgift ?? [])->firstWhere('kod', 'Officiell e-postadress')->uppgift[0] ?? '-') }}
- - - - - - - Visa på riksdagen.se -
- -
-
-
- - - - -
-

Uppdrag

-
- - -
- -
- - - @if ($selectedUppdragTab == 'current' && !empty($currentUppdrag)) -
- @foreach ($currentUppdrag as $uppdrag) -
-
-
-

{{ $uppdrag->roll_kod }}

- {{-- @if (!empty($uppdrag->uppgift[0]) && is_array($uppdrag->uppgift)) -

{{ implode(', ', $uppdrag->uppgift) }}

- @endif --}} -

- Typ: {{ ucfirst($uppdrag->typ) }} - @if (isset($uppdrag->status) && $uppdrag->status) - | Status: {{ $uppdrag->status }} - @endif -

-
-
-
Från: {{ Carbon\Carbon::parse($uppdrag->from)->format('Y-m-d') }}
- @if (!empty($uppdrag->tom)) -
Till: {{ Carbon\Carbon::parse($uppdrag->tom)->format('Y-m-d') }}
- @else -
Pågående
- @endif -
-
-
- @endforeach -
- @elseif($selectedUppdragTab == 'current' && empty($currentUppdrag)) -

Inga pågående uppdrag.

- @endif - - - @if ($selectedUppdragTab == 'previous' && !empty($previousUppdrag)) -
- @foreach ($previousUppdrag as $uppdrag) -
-
-
-

{{ $uppdrag->roll_kod }}

- {{-- @if (!empty($uppdrag->uppgift[0]) && is_array($uppdrag->uppgift)) -

{{ implode(', ', $uppdrag->uppgift) }}

- @endif --}} -

- Typ: {{ ucfirst($uppdrag->typ) }} - @if (isset($uppdrag->status) && $uppdrag->status) - | Status: {{ $uppdrag->status }} - @endif -

-
-
-
{{ Carbon\Carbon::parse($uppdrag->from)->format('Y-m-d') }} - - {{ Carbon\Carbon::parse($uppdrag->tom)->format('Y-m-d') }}
-
- {{ Carbon\Carbon::parse($uppdrag->from)->diffForHumans(Carbon\Carbon::parse($uppdrag->tom), true) }} -
-
-
-
- @endforeach -
- @elseif($selectedUppdragTab == 'previous' && empty($previousUppdrag)) -

Inga tidigare uppdrag registrerade.

- @endif -
- @if (!empty($votesByYear)) + @if (! empty($votesByYear)) -
+
-
- - - +
+ +
-

Voteringar

+

+ Voteringar +

- - @if ($selectedYear && isset($votesByYear[$selectedYear])) - -
- @if (!empty($this->votingStatistics)) + @if ($votesSelectedYear && isset($votesByYear[$votesSelectedYear])) + +
+ @if (! empty($this->votingStatistics))
-
- +
+
+
@php($totalVotes = array_sum($this->votingStatistics)) + @foreach ($this->votingStatistics as $voteType => $count) -
- {{ $voteType }} - {{ $count }} - ({{ round(($count / $totalVotes) * 100, 2) }}%) +
+ + {{ $voteType }} + + + {{ $count }} + ({{ round(($count / $totalVotes) * 100, 2) }}%) +
@endforeach +
- Totalt antal voteringar + class="flex justify-between items-center p-3 bg-blue-50 rounded border-t-2 border-blue-500" + > {{ array_sum($this->votingStatistics) }} + class="text-black font-bold" + > + Totalt antal voteringar + + + {{ $totalVotes }} +
@else -

Ingen voteringsstatistik tillgänglig för detta år.

+

+ Ingen voteringsstatistik tillgänglig för + detta år. +

@endif
- @endif - - @if ($selectedYear && isset($votesByYear[$selectedYear])) -
- + +
+
+ class="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase" + > + Datum + + class="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase" + > + Beteckning + + class="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase" + > + Punkt + + class="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase" + > + Röst + + - @foreach ($votesByYear[$selectedYear] as $vote) + @foreach ($votesByYear[$votesSelectedYear] as $vote) - + - + + + @@ -310,41 +334,441 @@
- Datum - Beteckning - Punkt - Röst
- {{ Carbon\Carbon::parse($vote->systemdatum)->format('Y-m-d') }} + + {{ \Carbon\Carbon::parse($vote->systemdatum)->format('Y-m-d') }} - + {{ $vote->beteckning }} {{ $vote->punkt }} + {{ $vote->punkt }} + + class="inline-flex px-2 py-1 text-xs font-semibold rounded-full {{ + $vote->rost === 'Ja' + ? 'bg-green-100 text-green-800' + : ($vote->rost === 'Nej' + ? 'bg-red-100 text-red-800' + : 'bg-gray-100 text-gray-800') + }}" + > {{ $vote->rost }}
+ @else +

+ Inga voteringar tillgängliga för detta år. +

@endif - @endif -
- - -
-
-
- - - - -
-

Biografi

-
-
- @foreach (collect($person->personuppgift->uppgift ?? [])->where('typ', 'biografi') as $bio) -
-
{{ $bio->kod }}
-
- {{ is_array($bio->uppgift) ? implode(', ', $bio->uppgift) : $bio->uppgift }}
- @endforeach -
+ @endif + + +
+
+
+ + + +
+

Motioner

+
+ + + @if (! empty($motionsByYear)) +
+ +
+ + @if ($motionsSelectedYear && isset($motionsByYear[$motionsSelectedYear])) +
+ + + + + + + + + + + + + @foreach ($motionsByYear[$motionsSelectedYear] as $motion) + + + + + + + + + @endforeach + +
+ Beteckning + + Titel + + Typ + + Datum + + Status + + Åtgärder +
+ {{ $motionsSelectedYear }}:{{ $motion->beteckning }} + +
+ {{ $motion->titel }} +
+ @if (! empty($motion->undertitel)) +
+ {{ $motion->undertitel }} +
+ @endif +
+ + {{ $motion->subtyp }} + + + {{ \Carbon\Carbon::parse($motion->datum)->format('Y-m-d') }} + + + {{ $motion->status }} + + + @if (! empty($motion->dokument_url_html)) + + + + + Läs + + @endif +
+
+ @else +

+ Inga motioner tillgängliga för detta år. +

+ @endif + @else +

+ Inga motioner tillgängliga för denna person. +

+ @endif +
+ + +
+
+
+ + + +
+

Uppdrag

+
+ + +
+ +
+ + + @if ($selectedUppdragTab == 'current' && ! empty($currentUppdrag)) +
+ @foreach ($currentUppdrag as $uppdrag) +
+
+
+

+ {{ $uppdrag->roll_kod }} +

+ {{-- + @if (!empty($uppdrag->uppgift[0]) && is_array($uppdrag->uppgift)) +

{{ implode(', ', $uppdrag->uppgift) }}

+ @endif + --}} +

+ + Typ: + + {{ ucfirst($uppdrag->typ) }} + @if (isset($uppdrag->status) && $uppdrag->status) + | + + Status: + + {{ $uppdrag->status }} + @endif +

+
+
+
+ Från: + {{ Carbon\Carbon::parse($uppdrag->from)->format('Y-m-d') }} +
+ + @if (! empty($uppdrag->tom)) +
+ Till: + {{ Carbon\Carbon::parse($uppdrag->tom)->format('Y-m-d') }} +
+ @else +
+ Pågående +
+ @endif +
+
+
+ @endforeach +
+ @elseif ($selectedUppdragTab == 'current' && empty($currentUppdrag)) +

Inga pågående uppdrag.

+ @endif + + + @if ($selectedUppdragTab == 'previous' && ! empty($previousUppdrag)) +
+ @foreach ($previousUppdrag as $uppdrag) +
+
+
+

+ {{ $uppdrag->roll_kod }} +

+ {{-- + @if (!empty($uppdrag->uppgift[0]) && is_array($uppdrag->uppgift)) +

{{ implode(', ', $uppdrag->uppgift) }}

+ @endif + --}} +

+ + Typ: + + {{ ucfirst($uppdrag->typ) }} + @if (isset($uppdrag->status) && $uppdrag->status) + | + + Status: + + {{ $uppdrag->status }} + @endif +

+
+
+
+ {{ Carbon\Carbon::parse($uppdrag->from)->format('Y-m-d') }} + - + {{ Carbon\Carbon::parse($uppdrag->tom)->format('Y-m-d') }} +
+
+ {{ Carbon\Carbon::parse($uppdrag->from)->diffForHumans(Carbon\Carbon::parse($uppdrag->tom), true) }} +
+
+
+
+ @endforeach +
+ @elseif ($selectedUppdragTab == 'previous' && empty($previousUppdrag)) +

+ Inga tidigare uppdrag registrerade. +

+ @endif +
+ + +
+
+
+ + + +
+

Biografi

+
+
+ @foreach (collect($person->personuppgift->uppgift ?? [])->where('typ', 'biografi') as $bio) +
+
+ {{ $bio->kod }} +
+
+ {{ is_array($bio->uppgift) ? implode(', ', $bio->uppgift) : $bio->uppgift }} +
+
+ @endforeach +
+
+ @else +
+ + + +

+ Person inte hittad +

+

+ Personen kunde inte hittas i databasen. +

+
+ @endif
-@else -
- - - -

Person inte hittad

-

Personen kunde inte hittas i databasen.

-
- @endif -
diff --git a/routes/web.php b/routes/web.php index 4b6aa58..8b88090 100644 --- a/routes/web.php +++ b/routes/web.php @@ -1,5 +1,10 @@ name('home'); Route::get('/ledamot', PersonSearch::class)->name('person.search'); Route::get('/ledamot/{personId}', PersonShow::class)->name('person.show'); +Route::get('/motion', MotionSearch::class)->name('motion.search'); +Route::get('/motion/{motionId}', MotionShow::class)->name('motion.show'); + Route::view('dashboard', 'dashboard') ->middleware(['auth', 'verified']) ->name('dashboard');