Add new models and page for motions

This commit is contained in:
2025-12-27 16:51:57 +01:00
parent 3ef975ad39
commit 45fe15eef2
36 changed files with 3753 additions and 355 deletions

13
.prettierrc Normal file
View File

@@ -0,0 +1,13 @@
{
"plugins": ["prettier-plugin-blade"],
"overrides": [
{
"files": ["*.blade.php"],
"options": {
"parser": "blade"
}
}
],
"singleQuote": true,
"tabWidth": 4
}

View File

@@ -0,0 +1,93 @@
<?php
namespace App\Console\Commands;
use App\Jobs\ImportRiksdagDataJob;
use Illuminate\Console\Command;
class ImportRiksdagData extends Command
{
/**
* The name and signature of the console command.
*/
protected $signature = 'riksdag:import
{file? : The path to the JSON file to import}
{--url= : URL to download ZIP file from}
{--queue : Run the import job in the background queue}';
/**
* The console command description.
*/
protected $description = 'Import Riksdag data from a JSON file or download and process a ZIP file from URL';
/**
* Execute the console command.
*/
public function handle()
{
$filePath = $this->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;
}
}

View File

@@ -0,0 +1,562 @@
<?php
namespace App\Jobs;
use App\Models\DokAktivitet;
use App\Models\DokBilaga;
use App\Models\DokForslag;
use App\Models\DokIntressent;
use App\Models\DokReferens;
use App\Models\Dokument;
use App\Models\DokUppgift;
use App\Models\Organ;
use App\Models\Person;
use App\Models\PersonUppdrag;
use App\Models\PersonUppgift;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use ZipArchive;
class ImportRiksdagDataJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
protected $url;
protected $isUrl;
protected $filePath;
public function __construct(string $urlOrPath, bool $isUrl = false)
{
$this->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;
}
}
}

View File

@@ -0,0 +1,146 @@
<?php
namespace App\Livewire\Motion;
use App\Enums\Parties as PartyEnum;
use App\Services\RiksdagenService;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Facades\Cache;
use Livewire\Attributes\Computed;
use Livewire\Attributes\Url;
use Livewire\Component;
use Livewire\WithPagination;
class Search extends Component
{
use WithPagination;
#[Url]
public $query = '';
#[Url]
public $party = '';
#[Url]
public $dateInterval = '';
private $paginator;
public $loading = false;
public function mount()
{
$this->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');
}
}

View File

@@ -0,0 +1,71 @@
<?php
namespace App\Livewire\Motion;
use App\Services\RiksdagenService;
use Illuminate\Support\Facades\Cache;
use Livewire\Attributes\Computed;
use Livewire\Component;
class Show extends Component
{
public $motionId;
public $motion;
private RiksdagenService $service;
public function mount($motionId)
{
$this->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');
}
}

View File

@@ -2,30 +2,35 @@
namespace App\Livewire\Person; namespace App\Livewire\Person;
use Livewire\Component;
use App\Services\RiksdagenService;
use App\Enums\Parties; use App\Enums\Parties;
use App\Services\RiksdagenService;
use Livewire\Component;
class Search extends Component class Search extends Component
{ {
public $firstName = ''; public $firstName = '';
public $lastName = ''; public $lastName = '';
public $party = ''; public $party = '';
public $results = []; public $results = [];
public $parties = []; public $parties = [];
public function mount() 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() public function search()
{ {
$service = app(RiksdagenService::class); $service = app(RiksdagenService::class);
$this->results = $service->searchPerson( $this->results = $service->searchPerson(
firstName: $this->firstName, firstName: $this->firstName,
lastName: $this->lastName, lastName: $this->lastName,
party: $this->party party: $this->party,
)->original; )->original;
} }

View File

@@ -2,11 +2,12 @@
namespace App\Livewire\Person; namespace App\Livewire\Person;
use Livewire\Component;
use App\Services\RiksdagenService; use App\Services\RiksdagenService;
use Livewire\Attributes\Computed;
use Asantibanez\LivewireCharts\Models\PieChartModel; use Asantibanez\LivewireCharts\Models\PieChartModel;
use Illuminate\Support\Facades\Cache;
use Livewire\Attributes\Computed;
use Livewire\Attributes\Lazy; use Livewire\Attributes\Lazy;
use Livewire\Component;
#[Lazy()] #[Lazy()]
class Show extends Component class Show extends Component
@@ -17,9 +18,15 @@ class Show extends Component
public $votes = []; public $votes = [];
public $motions = [];
public $votesByYear = []; public $votesByYear = [];
public $selectedYear; public $motionsByYear = [];
public $votesSelectedYear;
public $motionsSelectedYear;
public $selectedUppdragTab = 'current'; public $selectedUppdragTab = 'current';
@@ -27,47 +34,87 @@ class Show extends Component
public $previousUppdrag = []; public $previousUppdrag = [];
private RiksdagenService $service;
public function mount($personId) public function mount($personId)
{ {
$this->personId = $personId; $this->personId = $personId;
$service = app(RiksdagenService::class); $this->service = app(RiksdagenService::class);
$result = $service->searchPerson(mp_id: $personId); $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->person = $result->original->personlista->person ?? null;
$this->getPersonVotes(); $this->getPersonVotes();
$this->getPersonMotions();
$this->groupUppdrag(); $this->groupUppdrag();
} }
#[Computed()] #[Computed()]
public function riksdagenUrl() 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() public function getPersonVotes()
{ {
$service = app(RiksdagenService::class); $result = Cache::remember('person_votes_'.$this->personId, 24 * 60 * 60, function () {
$result = $service->searchVotes(mp_id: $this->personId); return $this->service->searchVotes(mp_id: $this->personId);
});
$votesList = $result->original->voteringlista->votering ?? []; $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) { foreach ($votesList as $vote) {
$year = explode('/', $vote->rm)[0]; // Extract year from rm like "2025/26" $rm = $vote->rm;
if (!isset($this->votesByYear[$year])) { if (! isset($this->votesByYear[$rm])) {
$this->votesByYear[$year] = []; $this->votesByYear[$rm] = [];
} }
$this->votesByYear[$year][] = $vote; $this->votesByYear[$rm][] = $vote;
} }
// Set default selected year to the most recent // Set default selected year to the most recent
if (!empty($this->votesByYear)) { if (! empty($this->votesByYear)) {
$this->selectedYear = max(array_keys($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) public function selectUppdragTab($tab)
@@ -77,7 +124,7 @@ class Show extends Component
public function groupUppdrag() public function groupUppdrag()
{ {
if (!$this->person || !isset($this->person->personuppdrag->uppdrag)) { if (! $this->person || ! isset($this->person->personuppdrag->uppdrag)) {
return; return;
} }
@@ -98,11 +145,11 @@ class Show extends Component
} }
// Sort by date (most recent first) // 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; 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; 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()] #[Computed()]
public function votingStatistics() public function votingStatistics()
{ {
if (!$this->selectedYear || !isset($this->votesByYear[$this->selectedYear])) { if (! $this->votesSelectedYear || ! isset($this->votesByYear[$this->votesSelectedYear])) {
return []; return [];
} }
$votes = $this->votesByYear[$this->selectedYear]; $votes = $this->votesByYear[$this->votesSelectedYear];
$statistics = []; $statistics = [];
foreach ($votes as $vote) { foreach ($votes as $vote) {
$voteType = $vote->rost; $voteType = $vote->rost;
if (!isset($statistics[$voteType])) { if (! isset($statistics[$voteType])) {
$statistics[$voteType] = 0; $statistics[$voteType] = 0;
} }
$statistics[$voteType]++; $statistics[$voteType]++;
@@ -133,8 +180,8 @@ class Show extends Component
{ {
$statistics = $this->votingStatistics; $statistics = $this->votingStatistics;
$pieChart = (new PieChartModel()) $pieChart = (new PieChartModel)
->setTitle('Voteringsstatistik för ' . $this->selectedYear) ->setTitle('Voteringsstatistik för '.$this->votesSelectedYear)
->setAnimated(true) ->setAnimated(true)
->withDataLabels(); ->withDataLabels();

20
app/Models/Anforande.php Normal file
View File

@@ -0,0 +1,20 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Anforande extends Model
{
protected $table = 'anforande';
public $timestamps = false;
public function dokument()
{
return $this->belongsTo(Dokument::class, 'dok_hangar_id', 'hangar_id');
}
public function person()
{
return $this->belongsTo(Person::class, 'intressent_id', 'intressent_id');
}
}

20
app/Models/Debatt.php Normal file
View File

@@ -0,0 +1,20 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Debatt extends Model
{
protected $table = 'debatt';
public $timestamps = false;
public function dokument()
{
return $this->belongsTo(Dokument::class, 'hangar_id', 'hangar_id');
}
public function person()
{
return $this->belongsTo(Person::class, 'intressent_id', 'intressent_id');
}
}

View File

@@ -0,0 +1,27 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class DokAktivitet extends Model
{
protected $table = 'dokaktivitet';
public $timestamps = false;
protected $fillable = [
'hangar_id',
'kod',
'datum',
'namn',
'status',
'ordning',
'process',
];
public function dokument()
{
return $this->belongsTo(Dokument::class, 'hangar_id', 'hangar_id');
}
}

28
app/Models/DokBilaga.php Normal file
View File

@@ -0,0 +1,28 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class DokBilaga extends Model
{
protected $table = 'dokbilaga';
public $timestamps = false;
protected $fillable = [
'hangar_id',
'dok_id',
'filnamn',
'titel',
'subtitel',
'filstorlek',
'filtyp',
'fil_url',
];
public function dokument()
{
return $this->belongsTo(Dokument::class, 'hangar_id', 'hangar_id');
}
}

34
app/Models/DokForslag.php Normal file
View File

@@ -0,0 +1,34 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class DokForslag extends Model
{
protected $table = 'dokforslag';
public $timestamps = false;
protected $fillable = [
'hangar_id',
'nummer',
'beteckning',
'lydelse',
'lydelse2',
'utskottet',
'kammaren',
'behandlas_i',
'behandlas_i_punkt',
'kammarbeslutstyp',
'intressent',
'avsnitt',
'grundforfattning',
'andringsforfattning',
];
public function dokument()
{
return $this->belongsTo(Dokument::class, 'hangar_id', 'hangar_id');
}
}

View File

@@ -0,0 +1,31 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class DokIntressent extends Model
{
protected $table = 'dokintressent';
public $timestamps = false;
protected $fillable = [
'hangar_id',
'intressent_id',
'namn',
'partibet',
'ordning',
'roll',
];
public function dokument()
{
return $this->belongsTo(Dokument::class, 'hangar_id', 'hangar_id');
}
public function person()
{
return $this->belongsTo(Person::class, 'intressent_id', 'intressent_id');
}
}

View File

@@ -0,0 +1,15 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class DokMotforslag extends Model
{
protected $table = 'dokmotforslag';
public $timestamps = false;
public function dokument()
{
return $this->belongsTo(Dokument::class, 'hangar_id', 'hangar_id');
}
}

View File

@@ -0,0 +1,31 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class DokReferens extends Model
{
protected $table = 'dokreferens';
public $timestamps = false;
protected $fillable = [
'hangar_id',
'referenstyp',
'ref_dok_id',
'uppgift',
'ref_dok_typ',
'ref_dok_rm',
'ref_dok_bet',
'ref_dok_titel',
'ref_dok_subtitel',
'ref_dok_subtyp',
'ref_dok_dokumentnamn',
];
public function dokument()
{
return $this->belongsTo(Dokument::class, 'hangar_id', 'hangar_id');
}
}

30
app/Models/DokUppgift.php Normal file
View File

@@ -0,0 +1,30 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class DokUppgift extends Model
{
protected $table = 'dokuppgift';
public $timestamps = false;
protected $fillable = [
'hangar_id',
'kod',
'namn',
'text',
'dok_id',
'systemdatum',
];
protected $casts = [
'systemdatum' => 'datetime',
];
public function dokument()
{
return $this->belongsTo(Dokument::class, 'hangar_id', 'hangar_id');
}
}

View File

@@ -0,0 +1,15 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class DokUtskottsforslag extends Model
{
protected $table = 'dokutskottsforslag';
public $timestamps = false;
public function dokument()
{
return $this->belongsTo(Dokument::class, 'hangar_id', 'hangar_id');
}
}

102
app/Models/Dokument.php Normal file
View File

@@ -0,0 +1,102 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Dokument extends Model
{
protected $table = 'dokument';
protected $primaryKey = 'hangar_id';
public $timestamps = false;
protected $fillable = [
'hangar_id',
'dok_id',
'rm',
'beteckning',
'typ',
'subtyp',
'doktyp',
'typrubrik',
'dokumentnamn',
'debattnamn',
'tempbeteckning',
'organ',
'mottagare',
'nummer',
'slutnummer',
'datum',
'systemdatum',
'publicerad',
'titel',
'subtitel',
'status',
'htmlformat',
'relaterat_id',
'source',
'sourceid',
'dokument_url_text',
'dokument_url_html',
'dokumentstatus_url_xml',
'utskottsforslag_url_xml',
'html',
];
public function utskottsforslag()
{
return $this->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');
}
}

31
app/Models/Organ.php Normal file
View File

@@ -0,0 +1,31 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Organ extends Model
{
protected $table = 'organ';
protected $primaryKey = 'id';
public $timestamps = false;
protected $fillable = [
'id',
'kod',
'namn',
'typ',
'status',
'sortering',
'namn_en',
'domän',
'beskrivning',
];
public function personUppdrag()
{
return $this->hasMany(PersonUppdrag::class, 'organ_kod', 'kod');
}
}

61
app/Models/Person.php Normal file
View File

@@ -0,0 +1,61 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Person extends Model
{
protected $table = 'person';
protected $primaryKey = 'intressent_id';
protected $keyType = 'string';
public $incrementing = false;
public $timestamps = false;
protected $fillable = [
'intressent_id',
'född_år',
'kön',
'efternamn',
'tilltalsnamn',
'sorteringsnamn',
'iort',
'parti',
'valkrets',
'status',
];
public function uppdrag()
{
return $this->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');
}
}

View File

@@ -0,0 +1,44 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class PersonUppdrag extends Model
{
protected $table = 'personuppdrag';
public $timestamps = false;
protected $fillable = [
'intressent_id',
'organ_kod',
'roll_kod',
'ordningsnummer',
'status',
'typ',
'from',
'tom',
'uppgift',
];
protected $casts = [
'from' => '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');
}
}

View File

@@ -0,0 +1,24 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class PersonUppgift extends Model
{
protected $table = 'personuppgift';
public $timestamps = false;
protected $fillable = [
'intressent_id',
'uppgift_kod',
'uppgift',
'uppgift_typ',
];
public function person()
{
return $this->belongsTo(Person::class, 'intressent_id', 'intressent_id');
}
}

31
app/Models/Planering.php Normal file
View File

@@ -0,0 +1,31 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Planering extends Model
{
protected $table = 'planering';
protected $primaryKey = 'nyckel';
public $timestamps = false;
protected $casts = [
'datum' => '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');
}
}

16
app/Models/Riksmote.php Normal file
View File

@@ -0,0 +1,16 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Riksmote extends Model
{
protected $table = 'riksmote';
protected $primaryKey = 'pk';
public $timestamps = false;
protected $casts = [
'start' => 'datetime',
'slut' => 'datetime',
];
}

16
app/Models/Roll.php Normal file
View File

@@ -0,0 +1,16 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Roll extends Model
{
protected $table = 'roll';
protected $primaryKey = 'pk';
public $timestamps = false;
public function personUppdrag()
{
return $this->hasMany(PersonUppdrag::class, 'roll_kod', 'kod');
}
}

20
app/Models/Votering.php Normal file
View File

@@ -0,0 +1,20 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Votering extends Model
{
protected $table = 'votering';
public $timestamps = false;
public function dokument()
{
return $this->belongsTo(Dokument::class, 'hangar_id', 'hangar_id');
}
public function person()
{
return $this->belongsTo(Person::class, 'intressent_id', 'intressent_id');
}
}

View File

@@ -30,8 +30,8 @@ class RiksdagenService
'enamn' => $lastName, 'enamn' => $lastName,
'utformat' => 'json', 'utformat' => 'json',
'sort' => 'sorteringsnamn', 'sort' => 'sorteringsnamn',
'sortorder' => 'asc' 'sortorder' => 'asc',
] ],
])->getBody()->getContents(); ])->getBody()->getContents();
$data = json_decode($response); $data = json_decode($response);
@@ -50,8 +50,86 @@ class RiksdagenService
'rm' => $date, 'rm' => $date,
'parti' => $party, 'parti' => $party,
'utformat' => 'json', '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(); ])->getBody()->getContents();
$data = json_decode($response); $data = json_decode($response);

View File

@@ -0,0 +1,340 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up()
{
// dokument
Schema::create('dokument', function (Blueprint $table) {
$table->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');
}
};

33
package-lock.json generated
View File

@@ -13,6 +13,9 @@
"tailwindcss": "^4.0.7", "tailwindcss": "^4.0.7",
"vite": "^7.0.4" "vite": "^7.0.4"
}, },
"devDependencies": {
"prettier-plugin-blade": "^2.1.21"
},
"optionalDependencies": { "optionalDependencies": {
"@rollup/rollup-linux-x64-gnu": "4.9.5", "@rollup/rollup-linux-x64-gnu": "4.9.5",
"@tailwindcss/oxide-linux-x64-gnu": "^4.0.1", "@tailwindcss/oxide-linux-x64-gnu": "^4.0.1",
@@ -2056,6 +2059,36 @@
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
"license": "MIT" "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": { "node_modules/proxy-from-env": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",

View File

@@ -19,5 +19,8 @@
"@rollup/rollup-linux-x64-gnu": "4.9.5", "@rollup/rollup-linux-x64-gnu": "4.9.5",
"@tailwindcss/oxide-linux-x64-gnu": "^4.0.1", "@tailwindcss/oxide-linux-x64-gnu": "^4.0.1",
"lightningcss-linux-x64-gnu": "^1.29.1" "lightningcss-linux-x64-gnu": "^1.29.1"
},
"devDependencies": {
"prettier-plugin-blade": "^2.1.21"
} }
} }

View File

@@ -6,30 +6,117 @@
Riksdagen App Riksdagen App
</h1> </h1>
<p class="text-lg text-gray-600 max-w-2xl mx-auto"> <p class="text-lg text-gray-600 max-w-2xl mx-auto">
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
</p> </p>
</div> </div>
<!-- Navigation Cards --> <!-- Navigation Cards -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 max-w-6xl mx-auto"> <div
class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 max-w-6xl mx-auto"
>
<!-- Person Search Card --> <!-- Person Search Card -->
<a wire:navigate href="{{ route('person.search') }}" class="group"> <a wire:navigate href="{{ route('person.search') }}" class="group">
<div class="bg-white rounded-lg shadow-md hover:shadow-xl transition-all duration-300 transform hover:-translate-y-1 border border-gray-200 overflow-hidden"> <div
class="bg-white rounded-lg shadow-md hover:shadow-xl transition-all duration-300 transform hover:-translate-y-1 border border-gray-200 overflow-hidden"
>
<div class="p-6"> <div class="p-6">
<div class="w-12 h-12 bg-blue-100 rounded-lg flex items-center justify-center mb-4 group-hover:bg-blue-200 transition-colors"> <div
<svg class="w-6 h-6 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24"> class="w-12 h-12 bg-blue-100 rounded-lg flex items-center justify-center mb-4 group-hover:bg-blue-200 transition-colors"
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"></path> >
<svg
class="w-6 h-6 text-blue-600"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"
></path>
</svg> </svg>
</div> </div>
<h3 class="text-xl font-semibold text-gray-900 mb-2">Ledamöter</h3> <h3 class="text-xl font-semibold text-gray-900 mb-2">
Ledamöter
</h3>
<p class="text-gray-600 mb-4"> <p class="text-gray-600 mb-4">
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
</p> </p>
<div class="flex items-center text-blue-600 group-hover:text-blue-700"> <div
<span class="text-sm font-medium">Sök ledamöter</span> class="flex items-center text-blue-600 group-hover:text-blue-700"
<svg class="w-4 h-4 ml-2 transform group-hover:translate-x-1 transition-transform" fill="none" stroke="currentColor" viewBox="0 0 24 24"> >
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path> <span class="text-sm font-medium">
Sök ledamöter
</span>
<svg
class="w-4 h-4 ml-2 transform group-hover:translate-x-1 transition-transform"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 5l7 7-7 7"
></path>
</svg>
</div>
</div>
</div>
</a>
<!-- Motion Search Card -->
<a wire:navigate href="{{ route('motion.search') }}" class="group">
<div
class="bg-white rounded-lg h-full shadow-md hover:shadow-xl transition-all duration-300 transform hover:-translate-y-1 border border-gray-200 overflow-hidden"
>
<div class="p-6">
<div
class="w-12 h-12 bg-green-100 rounded-lg flex items-center justify-center mb-4 group-hover:bg-green-200 transition-colors"
>
<svg
class="w-6 h-6 text-green-600"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
></path>
</svg>
</div>
<h3 class="text-xl font-semibold text-gray-900 mb-2">
Motioner
</h3>
<p class="text-gray-600 mb-4">
Sök bland motioner som lämnats till riksdagen av
ledamöterna
</p>
<div
class="flex items-center text-green-600 group-hover:text-green-700"
>
<span class="text-sm font-medium">
Sök motioner
</span>
<svg
class="w-4 h-4 ml-2 transform group-hover:translate-x-1 transition-transform"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 5l7 7-7 7"
></path>
</svg> </svg>
</div> </div>
</div> </div>
@@ -37,16 +124,33 @@
</a> </a>
<!-- Placeholder Card 1 --> <!-- Placeholder Card 1 -->
<div class="bg-white rounded-lg shadow-md border border-gray-200 overflow-hidden opacity-60"> <div
class="bg-white rounded-lg shadow-md border border-gray-200 overflow-hidden opacity-60"
>
<div class="p-6"> <div class="p-6">
<div class="w-12 h-12 bg-gray-100 rounded-lg flex items-center justify-center mb-4"> <div
<svg class="w-6 h-6 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"> class="w-12 h-12 bg-gray-100 rounded-lg flex items-center justify-center mb-4"
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v10a2 2 0 002 2h8a2 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"></path> >
<svg
class="w-6 h-6 text-gray-400"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 5H7a2 2 0 00-2 2v10a2 2 0 002 2h8a2 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"
></path>
</svg> </svg>
</div> </div>
<h3 class="text-xl font-semibold text-gray-500 mb-2">Propositioner</h3> <h3 class="text-xl font-semibold text-gray-500 mb-2">
Propositioner
</h3>
<p class="text-gray-400 mb-4"> <p class="text-gray-400 mb-4">
Sök och läs propositioner och andra dokument från riksdagen Sök och läs propositioner och andra dokument från
riksdagen
</p> </p>
<div class="flex items-center text-gray-400"> <div class="flex items-center text-gray-400">
<span class="text-sm font-medium">Kommer snart</span> <span class="text-sm font-medium">Kommer snart</span>
@@ -55,14 +159,30 @@
</div> </div>
<!-- Placeholder Card 2 --> <!-- Placeholder Card 2 -->
<div class="bg-white rounded-lg shadow-md border border-gray-200 overflow-hidden opacity-60"> <div
class="bg-white rounded-lg shadow-md border border-gray-200 overflow-hidden opacity-60"
>
<div class="p-6"> <div class="p-6">
<div class="w-12 h-12 bg-gray-100 rounded-lg flex items-center justify-center mb-4"> <div
<svg class="w-6 h-6 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"> class="w-12 h-12 bg-gray-100 rounded-lg flex items-center justify-center mb-4"
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"></path> >
<svg
class="w-6 h-6 text-gray-400"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"
></path>
</svg> </svg>
</div> </div>
<h3 class="text-xl font-semibold text-gray-500 mb-2">Statistik</h3> <h3 class="text-xl font-semibold text-gray-500 mb-2">
Statistik
</h3>
<p class="text-gray-400 mb-4"> <p class="text-gray-400 mb-4">
Se röststatistik och analyser för partier och ledamöter Se röststatistik och analyser för partier och ledamöter
</p> </p>
@@ -72,33 +192,31 @@
</div> </div>
</div> </div>
<!-- Placeholder Card 3 -->
<div class="bg-white rounded-lg shadow-md border border-gray-200 overflow-hidden opacity-60">
<div class="p-6">
<div class="w-12 h-12 bg-gray-100 rounded-lg flex items-center justify-center mb-4">
<svg class="w-6 h-6 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
</svg>
</div>
<h3 class="text-xl font-semibold text-gray-500 mb-2">Kalender</h3>
<p class="text-gray-400 mb-4">
Se kommande voteringar och riksdagsmöten
</p>
<div class="flex items-center text-gray-400">
<span class="text-sm font-medium">Kommer snart</span>
</div>
</div>
</div>
<!-- Placeholder Card 4 --> <!-- Placeholder Card 4 -->
<div class="bg-white rounded-lg shadow-md border border-gray-200 overflow-hidden opacity-60"> <div
class="bg-white rounded-lg shadow-md border border-gray-200 overflow-hidden opacity-60"
>
<div class="p-6"> <div class="p-6">
<div class="w-12 h-12 bg-gray-100 rounded-lg flex items-center justify-center mb-4"> <div
<svg class="w-6 h-6 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"> class="w-12 h-12 bg-gray-100 rounded-lg flex items-center justify-center mb-4"
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="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 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"></path> >
<svg
class="w-6 h-6 text-gray-400"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="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 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"
></path>
</svg> </svg>
</div> </div>
<h3 class="text-xl font-semibold text-gray-500 mb-2">Partier</h3> <h3 class="text-xl font-semibold text-gray-500 mb-2">
Partier
</h3>
<p class="text-gray-400 mb-4"> <p class="text-gray-400 mb-4">
Utforska partier och deras ståndpunkter i olika frågor Utforska partier och deras ståndpunkter i olika frågor
</p> </p>
@@ -109,14 +227,30 @@
</div> </div>
<!-- Placeholder Card 5 --> <!-- Placeholder Card 5 -->
<div class="bg-white rounded-lg shadow-md border border-gray-200 overflow-hidden opacity-60"> <div
class="bg-white rounded-lg shadow-md border border-gray-200 overflow-hidden opacity-60"
>
<div class="p-6"> <div class="p-6">
<div class="w-12 h-12 bg-gray-100 rounded-lg flex items-center justify-center mb-4"> <div
<svg class="w-6 h-6 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"> class="w-12 h-12 bg-gray-100 rounded-lg flex items-center justify-center mb-4"
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path> >
<svg
class="w-6 h-6 text-gray-400"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
></path>
</svg> </svg>
</div> </div>
<h3 class="text-xl font-semibold text-gray-500 mb-2">Sök Allmänt</h3> <h3 class="text-xl font-semibold text-gray-500 mb-2">
Sök Allmänt
</h3>
<p class="text-gray-400 mb-4"> <p class="text-gray-400 mb-4">
Sök i alla riksdagens dokument och voteringar Sök i alla riksdagens dokument och voteringar
</p> </p>
@@ -125,7 +259,6 @@
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -0,0 +1,361 @@
<div class="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-100">
<div class="container mx-auto py-12 px-4">
<!-- Breadcrumb Navigation -->
<nav class="mb-8" aria-label="Breadcrumb">
<div
class="bg-white rounded-lg shadow-sm border border-gray-200 px-4 py-3"
>
<ol class="flex items-center space-x-2 text-sm text-gray-600">
<li>
<a
wire:navigate
href="{{ route('home') }}"
class="text-blue-600 hover:text-blue-700 flex items-center"
>
<svg
class="w-4 h-4 mr-1"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"
></path>
</svg>
Hem
</a>
</li>
<li>
<svg
class="w-4 h-4 text-gray-400"
fill="currentColor"
viewBox="0 0 20 20"
>
<path
fill-rule="evenodd"
d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z"
clip-rule="evenodd"
></path>
</svg>
</li>
<li class="font-medium text-gray-900">Sök Motioner</li>
</ol>
</div>
</nav>
<!-- Header -->
<div class="text-center mb-8">
<h1 class="text-4xl font-bold text-gray-900 mb-4">Sök Motioner</h1>
<p class="text-lg text-gray-600">
Sök bland alla motioner som lämnats till riksdagen
</p>
</div>
<!-- Search Form -->
<div
class="bg-white rounded-lg shadow-md border border-gray-200 p-6 mb-8"
>
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
<!-- Query Search -->
<div>
<label
for="query"
class="block text-sm font-medium text-gray-700 mb-2"
>
Sökfras
</label>
<input
type="text"
id="query"
wire:model.live.debounce.500ms="query"
class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 text-black"
placeholder="Sök efter nyckelord..."
/>
</div>
<!-- Party Select -->
<div>
<label
for="party"
class="block text-sm font-medium text-gray-700 mb-2"
>
Parti
</label>
<select
id="party"
wire:model.live="party"
class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 text-black"
>
<option value="">Alla partier</option>
@foreach ($this->parties as $code => $name)
<option value="{{ $code }}">{{ $name }}</option>
@endforeach
</select>
</div>
<!-- Date Interval -->
<div>
<label
for="dateInterval"
class="block text-sm font-medium text-gray-700 mb-2"
>
Riksdagsår
</label>
<select
id="dateInterval"
wire:model.live="dateInterval"
class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 text-black"
>
<option value="">Alla år</option>
@foreach ($this->dateIntervals as $interval => $label)
<option value="{{ $interval }}">
{{ $label }}
</option>
@endforeach
</select>
</div>
</div>
<!-- Clear Filters Button -->
<div class="mt-4 flex justify-end">
<button
wire:click="clearFilters"
class="px-4 py-2 text-sm font-medium text-gray-700 bg-gray-100 border border-gray-300 rounded-md hover:bg-gray-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500"
>
Rensa filter
</button>
</div>
</div>
<!-- Loading State -->
<div class="hidden" wire:loading.block>
<div class="flex justify-center items-center py-16">
<div
class="w-12 h-12 border-4 border-t-blue-600 border-blue-200 rounded-full animate-spin"
></div>
<span class="ml-4 text-gray-600 font-medium">
Söker motioner...
</span>
</div>
</div>
<!-- Results -->
@if (! $loading)
@if ($this->motions->isNotEmpty())
<!-- Results Header -->
<div
class="bg-white rounded-lg shadow-md border border-gray-200 p-4 mb-6"
>
<div class="flex justify-between items-center">
<h2 class="text-lg font-medium text-gray-900">
Hittade {{ number_format($this->totalResults) }}
motioner
</h2>
<div class="text-sm text-gray-500">
Sida {{ $this->getPage() }} av
{{ $paginatedMotions->lastPage() }}
</div>
</div>
</div>
<!-- Motion Cards -->
<div class="space-y-4 mb-8">
@foreach ($paginatedMotions as $motion)
<div
class="bg-white rounded-lg shadow-md border border-gray-200 p-6 hover:shadow-lg transition-shadow"
>
<div
class="flex flex-col lg:flex-row lg:items-start gap-4"
>
<div class="flex-1">
<div
class="flex items-start justify-between mb-3"
>
<div>
<h3
class="text-lg font-semibold text-gray-900 mb-1"
>
<a
wire:navigate
href="{{ route('motion.show', $motion->dok_id) }}"
class="hover:text-blue-600 transition-colors"
>
{{ $motion->titel }}
</a>
</h3>
@if (! empty($motion->undertitel))
<p
class="text-sm text-gray-600 mb-2"
>
{{ $motion->undertitel }}
</p>
@endif
</div>
<span
class="inline-flex px-3 py-1 text-xs font-semibold rounded-full {{ $motion->subtyp == 'Partimotion' ? 'bg-blue-100 text-blue-800' : 'bg-green-100 text-green-800' }}"
>
{{ $motion->subtyp }}
</span>
</div>
<div
class="grid grid-cols-2 lg:grid-cols-4 gap-4 mb-4"
>
<div>
<span
class="text-xs font-medium text-gray-500 uppercase"
>
Beteckning
</span>
<p
class="text-sm font-medium text-gray-900"
>
{{ $motion->rm }}:{{ $motion->beteckning }}
</p>
</div>
<div>
<span
class="text-xs font-medium text-gray-500 uppercase"
>
Datum
</span>
<p class="text-sm text-gray-900">
{{ \Carbon\Carbon::parse($motion->datum)->format('Y-m-d') }}
</p>
</div>
<div>
<span
class="text-xs font-medium text-gray-500 uppercase"
>
Status
</span>
<span
class="inline-flex px-2 py-1 text-xs font-semibold rounded-full {{
$motion->status == 'Hänvisad'
? 'bg-yellow-100 text-yellow-800'
: ($motion->status == 'Avslutad'
? 'bg-green-100 text-green-800'
: 'bg-gray-100 text-gray-800')
}}"
>
{{ $motion->status }}
</span>
</div>
<div>
<span
class="text-xs font-medium text-gray-500 uppercase"
>
Utskott
</span>
<p class="text-sm text-gray-900">
{{ $motion->organ ?: '-' }}
</p>
</div>
</div>
@if (! empty($motion->summary))
<p
class="text-sm text-gray-700 line-clamp-3 mb-4"
>
{!! strip_tags($motion->summary, '<mark>') !!}
</p>
@endif
</div>
<!-- Actions -->
<div
class="flex-shrink-0 flex flex-row lg:flex-col gap-2"
>
<a
wire:navigate
href="{{ route('motion.show', $motion->dok_id) }}"
class="px-4 py-2 text-sm font-medium text-white bg-blue-600 rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 text-center"
>
Visa detaljer
</a>
@if (! empty($motion->dokument_url_html))
<a
href="https:{{ $motion->dokument_url_html }}"
target="_blank"
class="px-4 py-2 text-sm font-medium text-gray-700 bg-gray-100 rounded-md hover:bg-gray-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500 text-center"
>
Läs motion
</a>
@endif
</div>
</div>
</div>
@endforeach
</div>
<!-- Pagination -->
<div
class="bg-white rounded-lg shadow-md border border-gray-200 p-4"
>
@if ($paginatedMotions)
{{ $paginatedMotions->links() }}
@endif
</div>
@elseif (! empty($query) || ! empty($party) || ! empty($dateInterval))
<!-- No Results -->
<div
class="bg-white rounded-lg shadow-md border border-gray-200 p-8 text-center"
>
<svg
class="w-16 h-16 text-gray-400 mx-auto mb-4"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
></path>
</svg>
<h3 class="text-lg font-medium text-gray-900 mb-2">
Inga motioner hittades
</h3>
<p class="text-gray-600 mb-4">
Försök ändra dina sökkriterier.
</p>
<button
wire:click="clearFilters"
class="px-4 py-2 text-sm font-medium text-white bg-blue-600 rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
>
Rensa filter
</button>
</div>
@else
<!-- Initial State -->
<div
class="bg-white rounded-lg shadow-md border border-gray-200 p-8 text-center"
>
<svg
class="w-16 h-16 text-gray-400 mx-auto mb-4"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
></path>
</svg>
<h3 class="text-lg font-medium text-gray-900 mb-2">
Börja din sökning
</h3>
<p class="text-gray-600">
Använd sökfälten ovan för att hitta motioner.
</p>
</div>
@endif
@endif
</div>
</div>

View File

@@ -0,0 +1,486 @@
<div class="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-100">
<div class="container mx-auto py-12 px-4">
<!-- Breadcrumb Navigation -->
<nav class="mb-8" aria-label="Breadcrumb">
<div
class="bg-white rounded-lg shadow-sm border border-gray-200 px-4 py-3"
>
<ol class="flex items-center space-x-2 text-sm text-gray-600">
<li>
<a
wire:navigate
href="{{ route('home') }}"
class="text-blue-600 hover:text-blue-700 flex items-center"
>
<svg
class="w-4 h-4 mr-1"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"
></path>
</svg>
Hem
</a>
</li>
<li>
<svg
class="w-4 h-4 text-gray-400"
fill="currentColor"
viewBox="0 0 20 20"
>
<path
fill-rule="evenodd"
d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z"
clip-rule="evenodd"
></path>
</svg>
</li>
<li>
<a
wire:navigate
href="{{ route('motion.search') }}"
class="text-blue-600 hover:text-blue-700"
>
Sök Motioner
</a>
</li>
<li>
<svg
class="w-4 h-4 text-gray-400"
fill="currentColor"
viewBox="0 0 20 20"
>
<path
fill-rule="evenodd"
d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z"
clip-rule="evenodd"
></path>
</svg>
</li>
<li class="font-medium text-gray-900">
Motion
{{ $motion->rm ?? '' }}:{{ $motion->beteckning ?? $motionId }}
</li>
</ol>
</div>
</nav>
@if ($motion)
<!-- Motion Header -->
<div
class="bg-white rounded-lg shadow-md border border-gray-200 p-8 mb-8"
>
<div class="flex flex-col lg:flex-row lg:items-start gap-6">
<div class="flex-1">
<div class="flex items-start justify-between mb-4">
<div>
<h1
class="text-3xl font-bold text-gray-900 mb-2"
>
{{ $motion->titel }}
</h1>
@if (! empty($motion->undertitel))
<p class="text-lg text-gray-600 mb-4">
{{ $motion->undertitel }}
</p>
@endif
</div>
<span
class="inline-flex px-3 py-1 text-sm font-semibold rounded-full {{ $motion->subtyp == 'Partimotion' ? 'bg-blue-100 text-blue-800' : 'bg-green-100 text-green-800' }}"
>
{{ $motion->subtyp }}
</span>
</div>
<!-- Motion Details Grid -->
<div
class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 mb-6"
>
<div class="bg-gray-50 rounded-lg p-4">
<div
class="text-sm font-medium text-gray-500 mb-1"
>
Beteckning
</div>
<div class="text-gray-900 font-semibold">
{{ $motion->rm }}:{{ $motion->beteckning }}
</div>
</div>
<div class="bg-gray-50 rounded-lg p-4">
<div
class="text-sm font-medium text-gray-500 mb-1"
>
Datum
</div>
<div class="text-gray-900">
{{ \Carbon\Carbon::parse($motion->datum)->format('Y-m-d') }}
</div>
</div>
<div class="bg-gray-50 rounded-lg p-4">
<div
class="text-sm font-medium text-gray-500 mb-1"
>
Status
</div>
<span
class="inline-flex px-2 py-1 text-xs font-semibold rounded-full {{
$motion->status == 'Hänvisad'
? 'bg-yellow-100 text-yellow-800'
: ($motion->status == 'Avslutad'
? 'bg-green-100 text-green-800'
: 'bg-gray-100 text-gray-800')
}}"
>
{{ $motion->status }}
</span>
</div>
<div class="bg-gray-50 rounded-lg p-4">
<div
class="text-sm font-medium text-gray-500 mb-1"
>
Utskott
</div>
<div class="text-gray-900">
{{ $motion->organ ?: 'Ej tilldelat' }}
</div>
</div>
</div>
<!-- Action Buttons -->
<div class="flex flex-wrap gap-3">
@if (! empty($motion->dokument_url_html))
<a
href="https:{{ $motion->dokument_url_html }}"
target="_blank"
class="px-4 py-2 text-sm font-medium text-white bg-blue-600 rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
>
<svg
class="w-4 h-4 inline mr-1"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"
></path>
</svg>
Läs fullständig motion
</a>
@endif
<a
href="{{ $this->riksdagenUrl }}"
target="_blank"
class="px-4 py-2 text-sm font-medium text-gray-700 bg-gray-100 rounded-md hover:bg-gray-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500"
>
<svg
class="w-4 h-4 inline mr-1"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"
></path>
</svg>
Visa riksdag.se
</a>
</div>
</div>
</div>
</div>
<!-- Motion Summary -->
@if (! empty($motion->summary))
<div
class="bg-white rounded-lg shadow-md border border-gray-200 p-6 mb-8"
>
<div class="flex items-center mb-4">
<div
class="w-10 h-10 bg-blue-100 rounded-lg flex items-center justify-center mr-3"
>
<svg
class="w-5 h-5 text-blue-600"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
></path>
</svg>
</div>
<h2 class="text-xl font-bold text-gray-900">
Sammanfattning
</h2>
</div>
<div
class="prose max-w-none text-gray-700 whitespace-pre-line"
>
{!! strip_tags($motion->summary, '<mark>') !!}
</div>
</div>
@endif
<!-- Authors Section -->
@if ($this->authors->isNotEmpty())
<div
class="bg-white rounded-lg shadow-md border border-gray-200 p-6 mb-8"
>
<div class="flex items-center mb-4">
<div
class="w-10 h-10 bg-green-100 rounded-lg flex items-center justify-center mr-3"
>
<svg
class="w-5 h-5 text-green-600"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197m13.5-9a2.5 2.5 0 11-5 0 2.5 2.5 0 015 0z"
></path>
</svg>
</div>
<h2 class="text-xl font-bold text-gray-900">
Motionärer ({{ $this->authors->count() }})
</h2>
</div>
<div
class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4"
>
@foreach ($this->authors as $author)
<div class="bg-gray-50 rounded-lg p-4">
<div class="flex items-center justify-between">
<div>
<div
class="font-semibold text-gray-900"
>
{{ $author->namn }}
</div>
<div class="text-sm text-gray-600">
{{ $author->partibet }}
</div>
</div>
<a
wire:navigate
href="{{ route('person.show', $author->intressent_id) }}"
class="text-blue-600 hover:text-blue-700 text-sm font-medium"
>
Visa profil
</a>
</div>
</div>
@endforeach
</div>
</div>
@endif
<!-- File Attachments -->
@if ($this->attachments->isNotEmpty())
<div
class="bg-white rounded-lg shadow-md border border-gray-200 p-6 mb-8"
>
<div class="flex items-center mb-4">
<div
class="w-10 h-10 bg-purple-100 rounded-lg flex items-center justify-center mr-3"
>
<svg
class="w-5 h-5 text-purple-600"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M15.172 7l-6.586 6.586a2 2 0 102.828 2.828l6.414-6.586a4 4 0 00-5.656-5.656l-6.415 6.585a6 6 0 108.486 8.486L20.5 13"
></path>
</svg>
</div>
<h2 class="text-xl font-bold text-gray-900">Bilagor</h2>
</div>
<div class="space-y-3">
@foreach ($this->attachments as $file)
<div
class="flex items-center justify-between p-4 bg-gray-50 rounded-lg"
>
<div class="flex items-center">
<div class="flex-shrink-0">
@if ($file->typ == 'pdf')
<svg
class="w-8 h-8 text-red-600"
fill="currentColor"
viewBox="0 0 20 20"
>
<path
fill-rule="evenodd"
d="M4 4a2 2 0 012-2h4.586A2 2 0 0112 2.586L15.414 6A2 2 0 0116 7.414V16a2 2 0 01-2 2H6a2 2 0 01-2-2V4zm2 6a1 1 0 011-1h6a1 1 0 110 2H7a1 1 0 01-1-1zm1 3a1 1 0 100 2h6a1 1 0 100-2H7z"
clip-rule="evenodd"
></path>
</svg>
@elseif ($file->typ == 'docx')
<svg
class="w-8 h-8 text-blue-600"
fill="currentColor"
viewBox="0 0 20 20"
>
<path
fill-rule="evenodd"
d="M4 4a2 2 0 012-2h4.586A2 2 0 0112 2.586L15.414 6A2 2 0 0116 7.414V16a2 2 0 01-2 2H6a2 2 0 01-2-2V4zm2 6a1 1 0 011-1h6a1 1 0 110 2H7a1 1 0 01-1-1zm1 3a1 1 0 100 2h6a1 1 0 100-2H7z"
clip-rule="evenodd"
></path>
</svg>
@else
<svg
class="w-8 h-8 text-gray-600"
fill="currentColor"
viewBox="0 0 20 20"
>
<path
fill-rule="evenodd"
d="M4 4a2 2 0 012-2h4.586A2 2 0 0112 2.586L15.414 6A2 2 0 0116 7.414V16a2 2 0 01-2 2H6a2 2 0 01-2-2V4zm2 6a1 1 0 011-1h6a1 1 0 110 2H7a1 1 0 01-1-1zm1 3a1 1 0 100 2h6a1 1 0 100-2H7z"
clip-rule="evenodd"
></path>
</svg>
@endif
</div>
<div class="ml-4">
<div
class="text-sm font-medium text-gray-900"
>
{{ $file->namn }}
</div>
<div class="text-sm text-gray-500">
{{ strtoupper($file->typ) }}
{{ number_format($file->storlek / 1024, 1) }}
KB
</div>
</div>
</div>
<a
href="{{ $file->url }}"
target="_blank"
class="px-3 py-1 text-sm font-medium text-blue-600 bg-blue-50 rounded-md hover:bg-blue-100 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
>
Ladda ner
</a>
</div>
@endforeach
</div>
</div>
@endif
<!-- Additional Information -->
<div
class="bg-white rounded-lg shadow-md border border-gray-200 p-6"
>
<div class="flex items-center mb-4">
<div
class="w-10 h-10 bg-gray-100 rounded-lg flex items-center justify-center mr-3"
>
<svg
class="w-5 h-5 text-gray-600"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
></path>
</svg>
</div>
<h2 class="text-xl font-bold text-gray-900">
Teknisk information
</h2>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="bg-gray-50 rounded-lg p-4">
<div class="text-sm font-medium text-gray-500 mb-1">
Dokument-ID
</div>
<div class="text-gray-900 font-mono">
{{ $motion->dok_id }}
</div>
</div>
<div class="bg-gray-50 rounded-lg p-4">
<div class="text-sm font-medium text-gray-500 mb-1">
Publicerad
</div>
<div class="text-gray-900">
{{ \Carbon\Carbon::parse($motion->publicerad)->format('Y-m-d') }}
</div>
</div>
<div class="bg-gray-50 rounded-lg p-4">
<div class="text-sm font-medium text-gray-500 mb-1">
Systemdatum
</div>
<div class="text-gray-900">
{{ \Carbon\Carbon::parse($motion->systemdatum)->format('Y-m-d H:i') }}
</div>
</div>
<div class="bg-gray-50 rounded-lg p-4">
<div class="text-sm font-medium text-gray-500 mb-1">
Källa
</div>
<div class="text-gray-900">{{ $motion->kalla }}</div>
</div>
</div>
</div>
@else
<!-- Motion Not Found -->
<div
class="bg-white rounded-lg shadow-md border border-gray-200 p-8 text-center"
>
<svg
class="w-16 h-16 text-gray-400 mx-auto mb-4"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
></path>
</svg>
<h3 class="text-lg font-medium text-gray-900 mb-2">
Motion inte hittad
</h3>
<p class="text-gray-600 mb-4">
Motionen med ID {{ $motionId }} kunde inte hittas.
</p>
<a
wire:navigate
href="{{ route('motion.search') }}"
class="px-4 py-2 text-sm font-medium text-white bg-blue-600 rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
>
Tillbaka till sökning
</a>
</div>
@endif
</div>
</div>

View File

@@ -88,14 +88,15 @@
@foreach ($persons as $person) @foreach ($persons as $person)
<div <div
class="bg-gray-50 rounded-lg p-4 hover:bg-gray-100 transition-colors border border-gray-200"> class="bg-gray-50 rounded-lg p-4 hover:bg-gray-100 transition-colors border border-gray-200">
<a wire:navigate href="{{ route('person.show', $person->intressent_id) }}">
<div class="flex items-center"> <div class="flex items-center">
<img src="{{ $person->bild_url_80 ?? '' }}" alt="{{ $person->tilltalsnamn }}" <img src="{{ $person->bild_url_80 ?? '' }}" alt="{{ $person->tilltalsnamn }}"
class="w-16 h-16 rounded-lg object-cover mr-4 border border-gray-300"> class="w-16 h-16 rounded-lg object-cover mr-4 border border-gray-300">
<div class="flex-1"> <div class="flex-1">
<a wire:navigate href="{{ route('person.show', $person->intressent_id) }}" <span class="text-xl font-bold text-black transition-colors">
class="text-xl font-bold text-blue-600 hover:text-blue-700 transition-colors"> {{ $person->tilltalsnamn }} {{ $person->efternamn }}
{{ $person->tilltalsnamn }} {{ $person->efternamn }} ({{ $person->parti }}) ({{ $person->parti }})
</a> </span>
<div class="text-gray-600 mt-1 flex items-center gap-2"> <div class="text-gray-600 mt-1 flex items-center gap-2">
<img src="{{ App\Enums\Parties::from($person->parti)->logo() }}" <img src="{{ App\Enums\Parties::from($person->parti)->logo() }}"
width="32"> width="32">
@@ -109,22 +110,24 @@
d="M9 5l7 7-7 7"></path> d="M9 5l7 7-7 7"></path>
</svg> </svg>
</div> </div>
</a>
</div> </div>
@endforeach @endforeach
</div> </div>
</div> </div>
</div>
@elseif ($results && (!isset($results->personlista->person) || empty($results->personlista->person))) @elseif ($results && (!isset($results->personlista->person) || empty($results->personlista->person)))
<div class="bg-yellow-50 border border-yellow-200 rounded-lg p-6 max-w-4xl mx-auto"> <div class="bg-yellow-50 border border-yellow-200 rounded-lg p-6 max-w-4xl mx-auto">
<div class="flex items-center"> <div class="flex items-center">
<svg class="w-5 h-5 text-yellow-600 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg class="w-5 h-5 text-yellow-600 mr-2" fill="none" stroke="currentColor"
viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path> d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg> </svg>
<span class="text-yellow-800 font-medium">Inga ledamöter hittades med de angivna sökkriterier.</span> <span class="text-yellow-800 font-medium">Inga ledamöter hittades med de angivna
sökkriterier.</span>
</div> </div>
</div> </div>
@endif @endif
</div> </div>
</div> </div>
</div> </div>

View File

@@ -2,306 +2,330 @@
<div class="container mx-auto py-12 px-4"> <div class="container mx-auto py-12 px-4">
<!-- Breadcrumb Navigation --> <!-- Breadcrumb Navigation -->
<nav class="mb-8" aria-label="Breadcrumb"> <nav class="mb-8" aria-label="Breadcrumb">
<div class="bg-white rounded-lg shadow-sm border border-gray-200 px-4 py-3"> <div
class="bg-white rounded-lg shadow-sm border border-gray-200 px-4 py-3"
>
<ol class="flex items-center space-x-2 text-sm text-gray-600"> <ol class="flex items-center space-x-2 text-sm text-gray-600">
<li> <li>
<a wire:navigate href="{{ route('home') }}" class="text-blue-600 hover:text-blue-700 flex items-center"> <a
<svg class="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24"> wire:navigate
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" href="{{ route('home') }}"
d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"> class="text-blue-600 hover:text-blue-700 flex items-center"
</path> >
<svg
class="w-4 h-4 mr-1"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"
></path>
</svg> </svg>
Hem Hem
</a> </a>
</li> </li>
<li> <li>
<svg class="w-4 h-4 text-gray-400" fill="currentColor" viewBox="0 0 20 20"> <svg
<path fill-rule="evenodd" class="w-4 h-4 text-gray-400"
fill="currentColor"
viewBox="0 0 20 20"
>
<path
fill-rule="evenodd"
d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z"
clip-rule="evenodd"></path> clip-rule="evenodd"
></path>
</svg> </svg>
</li> </li>
<li> <li>
<a wire:navigate href="{{ route('person.search') }}" class="text-blue-600 hover:text-blue-700">Sök <a
Ledamöter</a> wire:navigate
href="{{ route('person.search') }}"
class="text-blue-600 hover:text-blue-700"
>
Sök Ledamöter
</a>
</li> </li>
<li> <li>
<svg class="w-4 h-4 text-gray-400" fill="currentColor" viewBox="0 0 20 20"> <svg
<path fill-rule="evenodd" class="w-4 h-4 text-gray-400"
fill="currentColor"
viewBox="0 0 20 20"
>
<path
fill-rule="evenodd"
d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z"
clip-rule="evenodd"></path> clip-rule="evenodd"
></path>
</svg> </svg>
</li> </li>
<li class="font-medium text-gray-900"> <li class="font-medium text-gray-900">
{{ $person->tilltalsnamn ?? 'Laddar...' }} {{ $person->efternamn ?? '' }} {{ $person->tilltalsnamn ?? 'Laddar...' }}
{{ $person->efternamn ?? '' }}
</li> </li>
</ol> </ol>
</div> </div>
</nav> </nav>
@if ($person) @if ($person)
<!-- Main Profile Section --> <!-- Main Profile Section -->
<div class="bg-white rounded-lg shadow-md border border-gray-200 p-8 mb-8"> <div
class="bg-white rounded-lg shadow-md border border-gray-200 p-8 mb-8"
>
<div class="flex flex-col md:flex-row gap-8"> <div class="flex flex-col md:flex-row gap-8">
<div class="md:w-48"> <div class="md:w-48">
<img src="{{ $person->bild_url_max ?? '' }}" alt="{{ $person->tilltalsnamn }}" <img
class="w-full h-64 md:w-48 object-cover rounded-lg shadow-lg border border-gray-200"> src="{{ $person->bild_url_max ?? '' }}"
alt="{{ $person->tilltalsnamn }}"
class="w-full h-64 md:w-48 object-cover rounded-lg shadow-lg border border-gray-200"
/>
<span class="text-xs text-gray-500 mt-2 block">
Foto: Sveriges Riksdag
</span>
</div> </div>
<div class="flex-1"> <div class="flex-1">
<div class="mb-6"> <div class="mb-6">
<h1 class="text-4xl font-bold text-gray-900 mb-2">{{ $person->tilltalsnamn }} <h1 class="text-4xl font-bold text-gray-900 mb-2">
{{ $person->efternamn }} ({{ $person->parti }})</h1> {{ $person->tilltalsnamn }}
{{ $person->efternamn }}
({{ $person->parti }})
</h1>
<div class="flex flex-wrap items-center gap-4 mb-4"> <div class="flex flex-wrap items-center gap-4 mb-4">
<span <span
class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-blue-100 text-blue-800 grap-2"> 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() }} {{ App\Enums\Parties::from($person->parti)->label() }}
<img src="{{ App\Enums\Parties::from($person->parti)->logo() }}" width="32"> <img
src="{{ App\Enums\Parties::from($person->parti)->logo() }}"
width="32"
/>
</span>
<span class="text-gray-600">
{{ $person->valkrets }} valkrets
</span> </span>
<span class="text-gray-600">{{ $person->valkrets }} valkrets</span>
</div> </div>
</div> </div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-6"> <div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-6">
<div class="bg-gray-50 rounded-lg p-4"> <div class="bg-gray-50 rounded-lg p-4">
<div class="text-sm font-medium text-gray-500 mb-1">Född</div> <div
<div class="text-gray-900">{{ $person->fodd_ar }}</div> class="text-sm font-medium text-gray-500 mb-1"
>
Född
</div>
<div class="text-gray-900">
{{ $person->fodd_ar }}
({{ Carbon\Carbon::parse($person->fodd_ar)->age }}
år)
</div>
</div> </div>
<div class="bg-gray-50 rounded-lg p-4"> <div class="bg-gray-50 rounded-lg p-4">
<div class="text-sm font-medium text-gray-500 mb-1">Kön</div> <div
<div class="text-gray-900">{{ ucfirst($person->kon) }}</div> class="text-sm font-medium text-gray-500 mb-1"
>
Kön
</div>
<div class="text-gray-900">
{{ ucfirst($person->kon) }}
</div>
</div> </div>
<div class="bg-gray-50 rounded-lg p-4"> <div class="bg-gray-50 rounded-lg p-4">
<div class="text-sm font-medium text-gray-500 mb-1">Status</div> <div
<div class="text-gray-900">{{ $person->status }}</div> class="text-sm font-medium text-gray-500 mb-1"
>
Status
</div>
<div class="text-gray-900">
{{ $person->status }}
</div>
</div> </div>
<div class="bg-gray-50 rounded-lg p-4"> <div class="bg-gray-50 rounded-lg p-4">
<div class="text-sm font-medium text-gray-500 mb-1">E-post</div> <div
class="text-sm font-medium text-gray-500 mb-1"
>
E-post
</div>
<div class="text-gray-900 text-sm"> <div class="text-gray-900 text-sm">
{{ str_replace('[på]', '@', collect($person->personuppgift->uppgift ?? [])->firstWhere('kod', 'Officiell e-postadress')->uppgift[0] ?? '-') }} {{ str_replace('[på]', '@', collect($person->personuppgift->uppgift ?? [])->firstWhere('kod', 'Officiell e-postadress')->uppgift[0] ?? '-') }}
</div> </div>
</div> </div>
</div> </div>
<a href="{{ $this->riksdagen_url }}" target="_blank"
class="inline-flex items-center px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors">
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14">
</path>
</svg>
Visa riksdagen.se
</a>
</div> </div>
</div> </div>
</div> </div>
<!-- Assignments Section --> @if (! empty($votesByYear))
<div class="bg-white rounded-lg shadow-md border border-gray-200 p-6 mb-8">
<div class="flex items-center mb-6">
<div class="w-10 h-10 bg-blue-100 rounded-lg flex items-center justify-center mr-3">
<svg class="w-5 h-5 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M21 13.255A23.931 23.931 0 0112 15c-3.183 0-6.22-.62-9-1.745M16 6V4a2 2 0 00-2-2h-4a2 2 0 00-2-2v2m8 0H8m8 0v2a2 2 0 01-2 2H10a2 2 0 01-2-2V6">
</path>
</svg>
</div>
<h2 class="text-2xl font-bold text-gray-900">Uppdrag</h2>
</div>
<!-- Uppdrag tabs -->
<div class="border-b border-gray-200 mb-6">
<nav class="-mb-px flex space-x-8">
<button wire:click="selectUppdragTab('current')"
class="py-3 px-1 border-b-2 font-medium text-sm transition-colors {{ $selectedUppdragTab == 'current' ? 'border-blue-500 text-blue-600' : 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300' }}">
<span class="flex items-center">
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
Pågående ({{ count($currentUppdrag) }})
</span>
</button>
<button wire:click="selectUppdragTab('previous')"
class="py-3 px-1 border-b-2 font-medium text-sm transition-colors {{ $selectedUppdragTab == 'previous' ? 'border-blue-500 text-blue-600' : 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300' }}">
<span class="flex items-center">
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
Tidigare ({{ count($previousUppdrag) }})
</span>
</button>
</nav>
</div>
<!-- Current assignments -->
@if ($selectedUppdragTab == 'current' && !empty($currentUppdrag))
<div class="space-y-3">
@foreach ($currentUppdrag as $uppdrag)
<div class="bg-green-50 border border-green-200 rounded-lg p-4">
<div class="flex justify-between items-start">
<div>
<h4 class="font-semibold text-green-800">{{ $uppdrag->roll_kod }}</h4>
{{-- @if (!empty($uppdrag->uppgift[0]) && is_array($uppdrag->uppgift))
<p class="text-sm text-green-700 mt-1">{{ implode(', ', $uppdrag->uppgift) }}</p>
@endif --}}
<p class="text-sm text-green-600 mt-2">
<span class="font-medium">Typ:</span> {{ ucfirst($uppdrag->typ) }}
@if (isset($uppdrag->status) && $uppdrag->status)
| <span class="font-medium">Status:</span> {{ $uppdrag->status }}
@endif
</p>
</div>
<div class="text-right text-sm text-green-600">
<div>Från: {{ Carbon\Carbon::parse($uppdrag->from)->format('Y-m-d') }}</div>
@if (!empty($uppdrag->tom))
<div>Till: {{ Carbon\Carbon::parse($uppdrag->tom)->format('Y-m-d') }}</div>
@else
<div class="font-semibold text-green-700">Pågående</div>
@endif
</div>
</div>
</div>
@endforeach
</div>
@elseif($selectedUppdragTab == 'current' && empty($currentUppdrag))
<p class="text-gray-500 italic">Inga pågående uppdrag.</p>
@endif
<!-- Previous assignments -->
@if ($selectedUppdragTab == 'previous' && !empty($previousUppdrag))
<div class="space-y-3">
@foreach ($previousUppdrag as $uppdrag)
<div class="bg-gray-50 border border-gray-200 rounded-lg p-4">
<div class="flex justify-between items-start">
<div>
<h4 class="font-semibold text-gray-800">{{ $uppdrag->roll_kod }}</h4>
{{-- @if (!empty($uppdrag->uppgift[0]) && is_array($uppdrag->uppgift))
<p class="text-sm text-gray-700 mt-1">{{ implode(', ', $uppdrag->uppgift) }}</p>
@endif --}}
<p class="text-sm text-gray-600 mt-2">
<span class="font-medium">Typ:</span> {{ ucfirst($uppdrag->typ) }}
@if (isset($uppdrag->status) && $uppdrag->status)
| <span class="font-medium">Status:</span> {{ $uppdrag->status }}
@endif
</p>
</div>
<div class="text-right text-sm text-gray-600">
<div>{{ Carbon\Carbon::parse($uppdrag->from)->format('Y-m-d') }} -
{{ Carbon\Carbon::parse($uppdrag->tom)->format('Y-m-d') }}</div>
<div class="text-xs text-gray-500 mt-1">
{{ Carbon\Carbon::parse($uppdrag->from)->diffForHumans(Carbon\Carbon::parse($uppdrag->tom), true) }}
</div>
</div>
</div>
</div>
@endforeach
</div>
@elseif($selectedUppdragTab == 'previous' && empty($previousUppdrag))
<p class="text-gray-500 italic">Inga tidigare uppdrag registrerade.</p>
@endif
</div>
@if (!empty($votesByYear))
<!-- Voting Section --> <!-- Voting Section -->
<div class="bg-white rounded-lg shadow-md border border-gray-200 p-6 mb-8"> <div
class="bg-white rounded-lg shadow-md border border-gray-200 p-6 mb-8"
>
<div class="flex items-center mb-6"> <div class="flex items-center mb-6">
<div class="w-10 h-10 bg-blue-100 rounded-lg flex items-center justify-center mr-3"> <div
<svg class="w-5 h-5 text-blue-600" fill="none" stroke="currentColor" class="w-10 h-10 bg-blue-100 rounded-lg flex items-center justify-center mr-3"
viewBox="0 0 24 24"> >
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" <svg
d="M9 5H7a2 2 0 00-2 2v10a2 2 0 002 2h8a2 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 2m-6 9l2 2 4-4"> class="w-5 h-5 text-blue-600"
</path> fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 5H7a2 2 0 00-2 2v10a2 2 0 002 2h8a2 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 2m-6 9l2 2 4-4"
/>
</svg> </svg>
</div> </div>
<h2 class="text-2xl font-bold text-gray-900">Voteringar</h2> <h2 class="text-2xl font-bold text-gray-900">
Voteringar
</h2>
</div> </div>
<!-- Year tabs --> <!-- Year tabs -->
<div class="border-b border-gray-200 mb-4"> <div class="border-b border-gray-200 mb-4">
<nav class="-mb-px flex space-x-8"> <nav class="-mb-px flex space-x-8">
@foreach (array_keys($votesByYear) as $year) @foreach (array_keys($votesByYear) as $year)
<button wire:click="selectYear('{{ $year }}')" <button
class="cursor-pointer py-2 px-1 border-b-2 font-medium text-sm {{ $selectedYear == $year ? 'border-blue-500 text-blue-600' : 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300' }}"> wire:click="selectVotesYear('{{ $year }}')"
class="cursor-pointer py-2 px-1 border-b-2 font-medium text-sm {{ $votesSelectedYear == $year ? 'border-blue-500 text-blue-600' : 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300' }}"
>
{{ $year }} {{ $year }}
</button> </button>
@endforeach @endforeach
</nav> </nav>
</div> </div>
@if ($votesSelectedYear && isset($votesByYear[$votesSelectedYear]))
@if ($selectedYear && isset($votesByYear[$selectedYear])) <!-- Voting Statistics -->
<!-- Voting Statistics Chart --> <div
<div class="mb-8 bg-gray-200 p-6 rounded-lg shadow border"> class="mb-8 bg-gray-200 p-6 rounded-lg shadow border"
@if (!empty($this->votingStatistics)) >
@if (! empty($this->votingStatistics))
<div class="flex flex-col lg:flex-row gap-6"> <div class="flex flex-col lg:flex-row gap-6">
<div style="height: 23rem;"> <div style="height: 23rem">
<livewire:livewire-pie-chart :pie-chart-model="$this->pieChartModel" <livewire:livewire-pie-chart
key="{{ 'pie-chart-' . $selectedYear }}" /> :pie-chart-model="$this->pieChartModel"
key="{{ 'pie-chart-' . $votesSelectedYear }}"
/>
</div> </div>
<div class="lg:w-1/2 my-auto"> <div class="lg:w-1/2 my-auto">
<div class="space-y-2"> <div class="space-y-2">
@php($totalVotes = array_sum($this->votingStatistics)) @php($totalVotes = array_sum($this->votingStatistics))
@foreach ($this->votingStatistics as $voteType => $count) @foreach ($this->votingStatistics as $voteType => $count)
<div class="flex justify-between items-center p-3 bg-gray-50 rounded"> <div
<span class="text-black font-medium">{{ $voteType }}</span> class="flex justify-between items-center p-3 bg-gray-50 rounded"
<span class="text-black text-lg font-bold">{{ $count }} >
({{ round(($count / $totalVotes) * 100, 2) }}%)</span> <span
class="text-black font-medium"
>
{{ $voteType }}
</span>
<span
class="text-black text-lg font-bold"
>
{{ $count }}
({{ round(($count / $totalVotes) * 100, 2) }}%)
</span>
</div> </div>
@endforeach @endforeach
<div <div
class="flex justify-between items-center p-3 bg-blue-50 rounded border-t-2 border-blue-500"> class="flex justify-between items-center p-3 bg-blue-50 rounded border-t-2 border-blue-500"
<span class="text-black font-bold">Totalt antal voteringar</span> >
<span <span
class="text-black text-lg font-bold">{{ array_sum($this->votingStatistics) }}</span> class="text-black font-bold"
>
Totalt antal voteringar
</span>
<span
class="text-black text-lg font-bold"
>
{{ $totalVotes }}
</span>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
@else @else
<p class="text-gray-500">Ingen voteringsstatistik tillgänglig för detta år.</p> <p class="text-gray-500">
Ingen voteringsstatistik tillgänglig för
detta år.
</p>
@endif @endif
</div> </div>
@endif
<!-- Votes table --> <!-- Votes table -->
@if ($selectedYear && isset($votesByYear[$selectedYear])) <div
<div class="overflow-x-auto" style="overflow-y: auto; display: block; max-height: 400px;"> class="overflow-x-auto overflow-y-auto max-h-[400px]"
<table class="min-w-full bg-white border border-gray-200"> >
<table
class="min-w-full bg-white border border-gray-200"
>
<thead class="bg-gray-50 sticky top-0"> <thead class="bg-gray-50 sticky top-0">
<tr> <tr>
<th <th
class="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> class="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase"
Datum</th> >
Datum
</th>
<th <th
class="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> class="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase"
Beteckning</th> >
Beteckning
</th>
<th <th
class="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> class="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase"
Punkt</th> >
Punkt
</th>
<th <th
class="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> class="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase"
Röst</th> >
Röst
</th>
</tr> </tr>
</thead> </thead>
<tbody class="divide-y divide-gray-100"> <tbody class="divide-y divide-gray-100">
@foreach ($votesByYear[$selectedYear] as $vote) @foreach ($votesByYear[$votesSelectedYear] as $vote)
<tr class="bg-gray-200"> <tr class="bg-gray-200">
<td class="px-4 py-2 text-sm text-gray-900"> <td
{{ Carbon\Carbon::parse($vote->systemdatum)->format('Y-m-d') }} class="px-4 py-2 text-sm text-gray-900"
>
{{ \Carbon\Carbon::parse($vote->systemdatum)->format('Y-m-d') }}
</td> </td>
<td class="px-4 py-2 text-sm"> <td class="px-4 py-2 text-sm">
<a href="{{ $vote->votering_url_xml }}" <a
class="text-blue-600 hover:underline" target="_blank"> href="{{ $vote->votering_url_xml }}"
class="text-blue-600 hover:underline"
target="_blank"
>
{{ $vote->beteckning }} {{ $vote->beteckning }}
</a> </a>
</td> </td>
<td class="px-4 py-2 text-sm text-gray-900">{{ $vote->punkt }}</td>
<td
class="px-4 py-2 text-sm text-gray-900"
>
{{ $vote->punkt }}
</td>
<td class="px-4 py-2 text-sm"> <td class="px-4 py-2 text-sm">
<span <span
class="inline-flex px-2 py-1 text-xs font-semibold rounded-full class="inline-flex px-2 py-1 text-xs font-semibold rounded-full {{
{{ $vote->rost === 'Ja' $vote->rost === 'Ja'
? 'bg-green-100 text-green-800' ? 'bg-green-100 text-green-800'
: ($vote->rost === 'Nej' : ($vote->rost === 'Nej'
? 'bg-red-100 text-red-800' ? 'bg-red-100 text-red-800'
: 'bg-gray-100 text-gray-800') }}"> : 'bg-gray-100 text-gray-800')
}}"
>
{{ $vote->rost }} {{ $vote->rost }}
</span> </span>
</td> </td>
@@ -310,18 +334,400 @@
</tbody> </tbody>
</table> </table>
</div> </div>
@else
<p class="text-gray-500">
Inga voteringar tillgängliga för detta år.
</p>
@endif @endif
</div>
@endif
<!-- Motions Section -->
<div
class="bg-white rounded-lg shadow-md border border-gray-200 p-6 mb-8"
>
<div class="flex items-center mb-6">
<div
class="w-10 h-10 bg-green-100 rounded-lg flex items-center justify-center mr-3"
>
<svg
class="w-5 h-5 text-green-600"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
></path>
</svg>
</div>
<h2 class="text-2xl font-bold text-gray-900">Motioner</h2>
</div>
<!-- Year tabs for motions -->
@if (! empty($motionsByYear))
<div class="border-b border-gray-200 mb-4">
<nav class="-mb-px flex space-x-8">
@foreach (array_keys($motionsByYear) as $year)
<button
wire:click="selectMotionsYear('{{ $year }}')"
class="cursor-pointer py-2 px-1 border-b-2 font-medium text-sm {{ $motionsSelectedYear == $year ? 'border-green-500 text-green-600' : 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300' }}"
>
{{ $year }}
({{ count($motionsByYear[$year]) }})
</button>
@endforeach
</nav>
</div>
@if ($motionsSelectedYear && isset($motionsByYear[$motionsSelectedYear]))
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
>
Beteckning
</th>
<th
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
>
Titel
</th>
<th
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
>
Typ
</th>
<th
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
>
Datum
</th>
<th
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
>
Status
</th>
<th
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
>
Åtgärder
</th>
</tr>
</thead>
<tbody
class="bg-white divide-y divide-gray-200"
>
@foreach ($motionsByYear[$motionsSelectedYear] as $motion)
<tr class="hover:bg-gray-50">
<td
class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900"
>
{{ $motionsSelectedYear }}:{{ $motion->beteckning }}
</td>
<td
class="px-6 py-4 text-sm text-gray-900"
>
<div
class="max-w-xs truncate"
title="{{ $motion->titel }}"
>
{{ $motion->titel }}
</div>
@if (! empty($motion->undertitel))
<div
class="text-xs text-gray-500 mt-1"
>
{{ $motion->undertitel }}
</div>
@endif
</td>
<td
class="px-6 py-4 whitespace-nowrap text-sm text-gray-500"
>
<span
class="inline-flex px-2 py-1 text-xs font-semibold rounded-full {{ $motion->subtyp == 'Partimotion' ? 'bg-blue-100 text-blue-800' : 'bg-gray-100 text-gray-800' }}"
>
{{ $motion->subtyp }}
</span>
</td>
<td
class="px-6 py-4 whitespace-nowrap text-sm text-gray-500"
>
{{ \Carbon\Carbon::parse($motion->datum)->format('Y-m-d') }}
</td>
<td
class="px-6 py-4 whitespace-nowrap text-sm text-gray-500"
>
<span
class="inline-flex px-2 py-1 text-xs font-semibold rounded-full {{
$motion->status == 'LagdTillHandlingarna'
? 'bg-yellow-100 text-yellow-800'
: ($motion->status == 'Avslutad'
? 'bg-green-100 text-green-800'
: 'bg-gray-100 text-gray-800')
}}"
>
{{ $motion->status }}
</span>
</td>
<td
class="px-6 py-4 whitespace-nowrap text-sm font-medium"
>
@if (! empty($motion->dokument_url_html))
<a
href="https:{{ $motion->dokument_url_html }}"
target="_blank"
class="text-green-600 hover:text-green-900 mr-4"
>
<svg
class="w-4 h-4 inline"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"
></path>
</svg>
Läs
</a>
@endif
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
@else
<p class="text-gray-500">
Inga motioner tillgängliga för detta år.
</p>
@endif
@else
<p class="text-gray-500">
Inga motioner tillgängliga för denna person.
</p>
@endif
</div>
<!-- Assignments Section -->
<div
class="bg-white rounded-lg shadow-md border border-gray-200 p-6 mb-8"
>
<div class="flex items-center mb-6">
<div
class="w-10 h-10 bg-blue-100 rounded-lg flex items-center justify-center mr-3"
>
<svg
class="w-5 h-5 text-blue-600"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M21 13.255A23.931 23.931 0 0112 15c-3.183 0-6.22-.62-9-1.745M16 6V4a2 2 0 00-2-2h-4a2 2 0 00-2-2v2m8 0H8m8 0v2a2 2 0 01-2 2H10a2 2 0 01-2-2V6"
></path>
</svg>
</div>
<h2 class="text-2xl font-bold text-gray-900">Uppdrag</h2>
</div>
<!-- Uppdrag tabs -->
<div class="border-b border-gray-200 mb-6">
<nav class="-mb-px flex space-x-8">
<button
wire:click="selectUppdragTab('current')"
class="py-3 px-1 border-b-2 font-medium text-sm transition-colors {{ $selectedUppdragTab == 'current' ? 'border-blue-500 text-blue-600' : 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300' }}"
>
<span class="flex items-center">
<svg
class="w-4 h-4 mr-2"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"
></path>
</svg>
Pågående ({{ count($currentUppdrag) }})
</span>
</button>
<button
wire:click="selectUppdragTab('previous')"
class="py-3 px-1 border-b-2 font-medium text-sm transition-colors {{ $selectedUppdragTab == 'previous' ? 'border-blue-500 text-blue-600' : 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300' }}"
>
<span class="flex items-center">
<svg
class="w-4 h-4 mr-2"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"
></path>
</svg>
Tidigare ({{ count($previousUppdrag) }})
</span>
</button>
</nav>
</div>
<!-- Current assignments -->
@if ($selectedUppdragTab == 'current' && ! empty($currentUppdrag))
<div class="space-y-3">
@foreach ($currentUppdrag as $uppdrag)
<div
class="bg-green-50 border border-green-200 rounded-lg p-4"
>
<div class="flex justify-between items-start">
<div>
<h4
class="font-semibold text-green-800"
>
{{ $uppdrag->roll_kod }}
</h4>
{{--
@if (!empty($uppdrag->uppgift[0]) && is_array($uppdrag->uppgift))
<p class="text-sm text-green-700 mt-1">{{ implode(', ', $uppdrag->uppgift) }}</p>
@endif
--}}
<p class="text-sm text-green-600 mt-2">
<span class="font-medium">
Typ:
</span>
{{ ucfirst($uppdrag->typ) }}
@if (isset($uppdrag->status) && $uppdrag->status)
|
<span class="font-medium">
Status:
</span>
{{ $uppdrag->status }}
@endif
</p>
</div>
<div
class="text-right text-sm text-green-600"
>
<div>
Från:
{{ Carbon\Carbon::parse($uppdrag->from)->format('Y-m-d') }}
</div>
@if (! empty($uppdrag->tom))
<div>
Till:
{{ Carbon\Carbon::parse($uppdrag->tom)->format('Y-m-d') }}
</div>
@else
<div
class="font-semibold text-green-700"
>
Pågående
</div>
@endif
</div>
</div>
</div>
@endforeach
</div>
@elseif ($selectedUppdragTab == 'current' && empty($currentUppdrag))
<p class="text-gray-500 italic">Inga pågående uppdrag.</p>
@endif
<!-- Previous assignments -->
@if ($selectedUppdragTab == 'previous' && ! empty($previousUppdrag))
<div class="space-y-3">
@foreach ($previousUppdrag as $uppdrag)
<div
class="bg-gray-50 border border-gray-200 rounded-lg p-4"
>
<div class="flex justify-between items-start">
<div>
<h4 class="font-semibold text-gray-800">
{{ $uppdrag->roll_kod }}
</h4>
{{--
@if (!empty($uppdrag->uppgift[0]) && is_array($uppdrag->uppgift))
<p class="text-sm text-gray-700 mt-1">{{ implode(', ', $uppdrag->uppgift) }}</p>
@endif
--}}
<p class="text-sm text-gray-600 mt-2">
<span class="font-medium">
Typ:
</span>
{{ ucfirst($uppdrag->typ) }}
@if (isset($uppdrag->status) && $uppdrag->status)
|
<span class="font-medium">
Status:
</span>
{{ $uppdrag->status }}
@endif
</p>
</div>
<div
class="text-right text-sm text-gray-600"
>
<div>
{{ Carbon\Carbon::parse($uppdrag->from)->format('Y-m-d') }}
-
{{ Carbon\Carbon::parse($uppdrag->tom)->format('Y-m-d') }}
</div>
<div class="text-xs text-gray-500 mt-1">
{{ Carbon\Carbon::parse($uppdrag->from)->diffForHumans(Carbon\Carbon::parse($uppdrag->tom), true) }}
</div>
</div>
</div>
</div>
@endforeach
</div>
@elseif ($selectedUppdragTab == 'previous' && empty($previousUppdrag))
<p class="text-gray-500 italic">
Inga tidigare uppdrag registrerade.
</p>
@endif @endif
</div> </div>
<!-- Biography Section --> <!-- Biography Section -->
<div class="bg-white rounded-lg shadow-md border border-gray-200 p-6 mb-8"> <div
class="bg-white rounded-lg shadow-md border border-gray-200 p-6 mb-8"
>
<div class="flex items-center mb-6"> <div class="flex items-center mb-6">
<div class="w-10 h-10 bg-blue-100 rounded-lg flex items-center justify-center mr-3"> <div
<svg class="w-5 h-5 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24"> class="w-10 h-10 bg-blue-100 rounded-lg flex items-center justify-center mr-3"
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" >
d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.746 0 3.332.477 4.5 1.253v13C19.832 18.477 18.246 18 16.5 18c-1.746 0-3.332.477-4.5 1.253"> <svg
</path> class="w-5 h-5 text-blue-600"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.746 0 3.332.477 4.5 1.253v13C19.832 18.477 18.246 18 16.5 18c-1.746 0-3.332.477-4.5 1.253"
></path>
</svg> </svg>
</div> </div>
<h2 class="text-2xl font-bold text-gray-900">Biografi</h2> <h2 class="text-2xl font-bold text-gray-900">Biografi</h2>
@@ -329,22 +735,40 @@
<div class="space-y-4"> <div class="space-y-4">
@foreach (collect($person->personuppgift->uppgift ?? [])->where('typ', 'biografi') as $bio) @foreach (collect($person->personuppgift->uppgift ?? [])->where('typ', 'biografi') as $bio)
<div class="bg-gray-50 rounded-lg p-4"> <div class="bg-gray-50 rounded-lg p-4">
<div class="font-semibold text-gray-900 mb-2">{{ $bio->kod }}</div> <div class="font-semibold text-gray-900 mb-2">
{{ $bio->kod }}
</div>
<div class="text-gray-700"> <div class="text-gray-700">
{{ is_array($bio->uppgift) ? implode(', ', $bio->uppgift) : $bio->uppgift }}</div> {{ is_array($bio->uppgift) ? implode(', ', $bio->uppgift) : $bio->uppgift }}
</div>
</div> </div>
@endforeach @endforeach
</div> </div>
</div> </div>
@else @else
<div class="bg-white rounded-lg shadow-md border border-gray-200 p-8 text-center"> <div
<svg class="w-16 h-16 text-gray-400 mx-auto mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"> class="bg-white rounded-lg shadow-md border border-gray-200 p-8 text-center"
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" >
d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path> <svg
class="w-16 h-16 text-gray-400 mx-auto mb-4"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
></path>
</svg> </svg>
<h3 class="text-lg font-medium text-gray-900 mb-2">Person inte hittad</h3> <h3 class="text-lg font-medium text-gray-900 mb-2">
<p class="text-gray-600">Personen kunde inte hittas i databasen.</p> Person inte hittad
</h3>
<p class="text-gray-600">
Personen kunde inte hittas i databasen.
</p>
</div> </div>
@endif @endif
</div> </div>
</div> </div>

View File

@@ -1,5 +1,10 @@
<?php <?php
use App\Livewire\HomePage;
use App\Livewire\Motion\Search as MotionSearch;
use App\Livewire\Motion\Show as MotionShow;
use App\Livewire\Person\Search as PersonSearch;
use App\Livewire\Person\Show as PersonShow;
use App\Livewire\Settings\Appearance; use App\Livewire\Settings\Appearance;
use App\Livewire\Settings\Password; use App\Livewire\Settings\Password;
use App\Livewire\Settings\Profile; use App\Livewire\Settings\Profile;
@@ -7,15 +12,14 @@ use App\Livewire\Settings\TwoFactor;
use Illuminate\Support\Facades\Route; use Illuminate\Support\Facades\Route;
use Laravel\Fortify\Features; use Laravel\Fortify\Features;
use App\Livewire\HomePage;
use App\Livewire\Person\Search as PersonSearch;
use App\Livewire\Person\Show as PersonShow;
Route::get('/', HomePage::class)->name('home'); Route::get('/', HomePage::class)->name('home');
Route::get('/ledamot', PersonSearch::class)->name('person.search'); Route::get('/ledamot', PersonSearch::class)->name('person.search');
Route::get('/ledamot/{personId}', PersonShow::class)->name('person.show'); 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') Route::view('dashboard', 'dashboard')
->middleware(['auth', 'verified']) ->middleware(['auth', 'verified'])
->name('dashboard'); ->name('dashboard');