mirror of
https://github.com/Djeeberjr/fw-anwesenheit.git
synced 2026-04-30 18:49:09 +00:00
Compare commits
6 Commits
1ea70e4993
...
ebbec7885e
| Author | SHA1 | Date | |
|---|---|---|---|
| ebbec7885e | |||
| 7ecd2052d8 | |||
| 96512c8a12 | |||
| c3eaff03d9 | |||
| 4bf89626b9 | |||
| 7c0c0699b5 |
@@ -12,3 +12,7 @@ target = "riscv32imac-unknown-none-elf"
|
|||||||
|
|
||||||
[unstable]
|
[unstable]
|
||||||
build-std = ["alloc", "core"]
|
build-std = ["alloc", "core"]
|
||||||
|
|
||||||
|
[env]
|
||||||
|
WIFI_PASSWD = "hunter22"
|
||||||
|
WIFI_SSID = "fwa"
|
||||||
|
|||||||
@@ -2,7 +2,9 @@ use embassy_executor::Spawner;
|
|||||||
use embassy_time::{Duration, Timer};
|
use embassy_time::{Duration, Timer};
|
||||||
use esp_hal::gpio::{Output, OutputConfig};
|
use esp_hal::gpio::{Output, OutputConfig};
|
||||||
use esp_hal::peripherals::{GPIO3, GPIO14, WIFI};
|
use esp_hal::peripherals::{GPIO3, GPIO14, WIFI};
|
||||||
use esp_wifi::wifi::{AccessPointConfiguration, Configuration, WifiController, WifiEvent, WifiState};
|
use esp_wifi::wifi::{
|
||||||
|
AccessPointConfiguration, Configuration, WifiController, WifiEvent, WifiState,
|
||||||
|
};
|
||||||
use esp_wifi::{EspWifiRngSource, EspWifiTimerSource, wifi::Interfaces};
|
use esp_wifi::{EspWifiRngSource, EspWifiTimerSource, wifi::Interfaces};
|
||||||
use static_cell::make_static;
|
use static_cell::make_static;
|
||||||
|
|
||||||
@@ -46,7 +48,9 @@ async fn connection(mut controller: WifiController<'static>) {
|
|||||||
}
|
}
|
||||||
if !matches!(controller.is_started(), Ok(true)) {
|
if !matches!(controller.is_started(), Ok(true)) {
|
||||||
let client_config = Configuration::AccessPoint(AccessPointConfiguration {
|
let client_config = Configuration::AccessPoint(AccessPointConfiguration {
|
||||||
ssid: "esp-wifi".try_into().unwrap(),
|
ssid: env!("WIFI_SSID").try_into().unwrap(),
|
||||||
|
password: env!("WIFI_PASSWD").try_into().unwrap(),
|
||||||
|
auth_method: esp_wifi::wifi::AuthMethod::WPA2Personal,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
controller.set_configuration(&client_config).unwrap();
|
controller.set_configuration(&client_config).unwrap();
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ impl<T: Persistence> IDStore<T> {
|
|||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn persist_mapping(&mut self) {
|
pub async fn persist_mapping(&mut self) {
|
||||||
self.persistence_layer.save_mapping(&self.mapping).await
|
self.persistence_layer.save_mapping(&self.mapping).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use esp_println::dbg;
|
use esp_println::dbg;
|
||||||
|
use log::error;
|
||||||
use picoserve::{
|
use picoserve::{
|
||||||
extract::{Json, Query, State},
|
extract::{Json, Query, State},
|
||||||
response::{self, IntoResponse},
|
response::{self, IntoResponse},
|
||||||
@@ -41,11 +42,23 @@ pub async fn add_mapping(
|
|||||||
) -> impl IntoResponse {
|
) -> impl IntoResponse {
|
||||||
let mut store = state.store.lock().await;
|
let mut store = state.store.lock().await;
|
||||||
store.mapping.add_mapping(data.id, data.name);
|
store.mapping.add_mapping(data.id, data.name);
|
||||||
|
store.persist_mapping().await;
|
||||||
}
|
}
|
||||||
|
|
||||||
// SSE /api/idevent
|
// SSE /api/idevent
|
||||||
pub async fn get_idevent(State(state): State<AppState>) -> impl IntoResponse {
|
pub async fn get_idevent(
|
||||||
response::EventStream(IDEvents(state.chan.subscriber().unwrap()))
|
State(state): State<AppState>,
|
||||||
|
) -> Result<impl IntoResponse, impl IntoResponse> {
|
||||||
|
match state.chan.subscriber() {
|
||||||
|
Ok(chan) => Ok(response::EventStream(IDEvents(chan))),
|
||||||
|
Err(e) => {
|
||||||
|
error!("Failed to create SSE: {:?}", e);
|
||||||
|
Err((
|
||||||
|
response::StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
|
"Internal server error",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GET /api/days
|
// GET /api/days
|
||||||
@@ -68,7 +81,6 @@ pub async fn get_day(
|
|||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
Query(QueryDay { timestamp, day }): Query<QueryDay>,
|
Query(QueryDay { timestamp, day }): Query<QueryDay>,
|
||||||
) -> Result<impl IntoResponse, impl IntoResponse> {
|
) -> Result<impl IntoResponse, impl IntoResponse> {
|
||||||
|
|
||||||
let parsed_day = timestamp
|
let parsed_day = timestamp
|
||||||
.map(Day::new_from_timestamp)
|
.map(Day::new_from_timestamp)
|
||||||
.or_else(|| day.map(Day::new))
|
.or_else(|| day.map(Day::new))
|
||||||
|
|||||||
@@ -55,6 +55,13 @@
|
|||||||
"first": "Juniper",
|
"first": "Juniper",
|
||||||
"last": "Voss"
|
"last": "Voss"
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"A0B1DB0D133B",
|
||||||
|
{
|
||||||
|
"first": "Öäü",
|
||||||
|
"last": "ßẞ"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
"days": [
|
"days": [
|
||||||
@@ -62,7 +69,8 @@
|
|||||||
"date": 20372,
|
"date": 20372,
|
||||||
"ids": [
|
"ids": [
|
||||||
"123456789ABC",
|
"123456789ABC",
|
||||||
"A1B2C3D4E5F6"
|
"A1B2C3D4E5F6",
|
||||||
|
"A0B1DB0D133B"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -77,7 +85,8 @@
|
|||||||
"ids": [
|
"ids": [
|
||||||
"654321FEDCBA",
|
"654321FEDCBA",
|
||||||
"DEADBEEFCAFE",
|
"DEADBEEFCAFE",
|
||||||
"BADA55C0FFEE"
|
"BADA55C0FFEE",
|
||||||
|
"A0B1DB0D133B"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,41 +1,105 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Modal from "./Modal.svelte";
|
import Modal from "./Modal.svelte";
|
||||||
|
|
||||||
let { onSubmitted }: { onSubmitted?: (from: Date, to: Date) => void } = $props();
|
let { onSubmitted }: { onSubmitted?: (from: Date, to: Date) => void } =
|
||||||
|
$props();
|
||||||
|
|
||||||
let modal: Modal;
|
let modal: Modal;
|
||||||
|
|
||||||
let fromDate: string | undefined = $state();
|
let fromDate: string | undefined = $state();
|
||||||
let toDate: string | undefined = $state();
|
let toDate: string | undefined = $state();
|
||||||
|
|
||||||
|
let selectedYear: number = $state(new Date().getFullYear());
|
||||||
|
|
||||||
|
let selectedTab = $state(0);
|
||||||
|
|
||||||
export function open() {
|
export function open() {
|
||||||
modal.open();
|
modal.open();
|
||||||
}
|
}
|
||||||
|
|
||||||
function onsubmit(e: SubmitEvent) {
|
function generateYears() {
|
||||||
if (!fromDate || !toDate){
|
const currentYear = new Date().getFullYear();
|
||||||
e.preventDefault();
|
const startingYear = 2020;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let from = new Date(fromDate);
|
|
||||||
let to = new Date(toDate);
|
|
||||||
|
|
||||||
onSubmitted?.(from,to);
|
return Array.from(
|
||||||
|
new Array(currentYear + 1 - startingYear),
|
||||||
|
(_, i) => i + startingYear,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onsubmit(e: SubmitEvent) {
|
||||||
|
let from: Date;
|
||||||
|
let to: Date;
|
||||||
|
|
||||||
|
switch (selectedTab) {
|
||||||
|
case 0:
|
||||||
|
if (!fromDate || !toDate) {
|
||||||
|
e.preventDefault();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
from = new Date(fromDate);
|
||||||
|
to = new Date(toDate);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
from = new Date(selectedYear, 0);
|
||||||
|
to = new Date(selectedYear + 1, 0);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.error("Invalid tab");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
onSubmitted?.(from, to);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Modal bind:this={modal}>
|
<Modal bind:this={modal}>
|
||||||
|
<div class="flex">
|
||||||
|
<button
|
||||||
|
onclick={() => {
|
||||||
|
selectedTab = 0;
|
||||||
|
}}
|
||||||
|
class="tab {selectedTab === 0 ? 'tab-active' : ''}"
|
||||||
|
>
|
||||||
|
Datum
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onclick={() => {
|
||||||
|
selectedTab = 1;
|
||||||
|
}}
|
||||||
|
class="tab {selectedTab === 1 ? 'tab-active' : ''}"
|
||||||
|
>
|
||||||
|
Jahr
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
<form method="dialog" {onsubmit} class="flex flex-col">
|
<form method="dialog" {onsubmit} class="flex flex-col">
|
||||||
<label class="form-row">
|
{#if selectedTab === 0}
|
||||||
<span>Von:</span>
|
<div>
|
||||||
<input type="date" class="form-input" bind:value={fromDate} />
|
<label class="form-row">
|
||||||
</label>
|
<span>Von:</span>
|
||||||
|
<input type="date" class="form-input" bind:value={fromDate} />
|
||||||
|
</label>
|
||||||
|
|
||||||
<label class="form-row">
|
<label class="form-row">
|
||||||
<span>Bis:</span>
|
<span>Bis:</span>
|
||||||
<input type="date" class="form-input" bind:value={toDate} />
|
<input type="date" class="form-input" bind:value={toDate} />
|
||||||
</label>
|
</label>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{#if selectedTab === 1}
|
||||||
|
<div>
|
||||||
|
<label class="form-row">
|
||||||
|
<span>Kalendar Jahr:</span>
|
||||||
|
<select class="form-input" bind:value={selectedYear}>
|
||||||
|
{#each generateYears() as year}
|
||||||
|
<option value={year}>{year}</option>
|
||||||
|
{/each}
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
<div class="flex justify-end mt-3">
|
<div class="flex justify-end mt-3">
|
||||||
<button
|
<button
|
||||||
@@ -60,11 +124,19 @@
|
|||||||
<style scoped>
|
<style scoped>
|
||||||
@reference "../app.css";
|
@reference "../app.css";
|
||||||
|
|
||||||
|
.tab {
|
||||||
|
@apply px-4 py-2 rounded-t-lg bg-indigo-600 hover:bg-indigo-700 font-medium border-b-2 border-transparent cursor-pointer transition-colors duration-200;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-active {
|
||||||
|
@apply px-4 py-2 bg-indigo-500 font-semibold border-b-2 border-blue-600 shadow-sm cursor-pointer;
|
||||||
|
}
|
||||||
|
|
||||||
.form-row {
|
.form-row {
|
||||||
@apply flex justify-between;
|
@apply flex justify-between my-1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-input {
|
.form-input {
|
||||||
@apply ml-10;
|
@apply ml-20;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -11,10 +11,10 @@ type InputRows = RowObject[];
|
|||||||
|
|
||||||
export function generateCSVString(input: InputRows, opts: CSVOptions = {}): string {
|
export function generateCSVString(input: InputRows, opts: CSVOptions = {}): string {
|
||||||
const {
|
const {
|
||||||
delimiter = ",",
|
delimiter = ";",
|
||||||
headerOrder,
|
headerOrder,
|
||||||
eol = "\r\n",
|
eol = "\r\n",
|
||||||
includeBOM = false,
|
includeBOM = true,
|
||||||
nullString = "",
|
nullString = "",
|
||||||
} = opts;
|
} = opts;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user