Skip to content

Commit

Permalink
Refactor firecallitem (#13)
Browse files Browse the repository at this point in the history
* Starting point for new firecallitem base class

* Extend baseclass

* Dockerfile and devcontainer

* Update rendering

* Rewerite components

* Add login page before rendering any other pages

* Add about to login page

* Use a page to switch firecalls

* Add diary

* Add progress

* Add polyline check
  • Loading branch information
r00tat authored Dec 23, 2023
1 parent 94dbf97 commit de97515
Show file tree
Hide file tree
Showing 40 changed files with 1,937 additions and 144 deletions.
22 changes: 22 additions & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
// README at: https://github.com/devcontainers/templates/tree/main/src/typescript-node
{
"name": "Node.js & TypeScript",
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
"image": "mcr.microsoft.com/devcontainers/typescript-node:1-20-bullseye",

// Features to add to the dev container. More info: https://containers.dev/features.
// "features": {},

// Use 'forwardPorts' to make a list of ports inside the container available locally.
"forwardPorts": [3000]

// Use 'postCreateCommand' to run commands after the container is created.
// "postCreateCommand": "yarn install",

// Configure tool-specific properties.
// "customizations": {},

// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
// "remoteUser": "root"
}
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# https://nextjs.org/docs/deployment
# Install dependencies only when needed
FROM node:18-alpine AS base
FROM node:20-alpine AS base
FROM base AS deps
# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
RUN apk add --no-cache libc6-compat
Expand Down
100 changes: 57 additions & 43 deletions src/components/FirecallItems/FirecallItemDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,14 @@ import TextField from '@mui/material/TextField';
import moment from 'moment';
import React, { useState } from 'react';
import ConfirmDialog from '../dialogs/ConfirmDialog';
import MyDateTimePicker from '../inputs/DateTimePicker';
import { FirecallItem } from '../firebase/firestore';
import { firecallItemInfo, firecallItems } from './infos/firecallitems';
import { FirecallItemInfo } from './infos/types';
import MyDateTimePicker from '../inputs/DateTimePicker';
import { fcItemClasses, fcItemNames, getItemClass } from './elements';
import { FirecallItemBase } from './elements/FirecallItemBase';
import { CheckBox } from '@mui/icons-material';
import FormGroup from '@mui/material/FormGroup';
import FormControlLabel from '@mui/material/FormControlLabel';
import Checkbox from '@mui/material/Checkbox';

export interface FirecallItemDialogOptions {
onClose: (item?: FirecallItem) => void;
Expand All @@ -31,18 +35,13 @@ export default function FirecallItemDialog({
type: itemType,
}: FirecallItemDialogOptions) {
const [open, setOpen] = useState(true);
const [item, setFirecallItem] = useState<FirecallItem>(
itemDefault || firecallItemInfo(itemType).factory()
const [item, setFirecallItem] = useState<FirecallItemBase>(
getItemClass(itemDefault || ({ type: itemType } as FirecallItem))
);
const [confirmDelete, setConfirmDelete] = useState(false);

const itemInfo: FirecallItemInfo = firecallItemInfo(item.type);

const setItemField = (field: string, value: any) => {
setFirecallItem((prev) => ({
...prev,
[field]: value,
}));
setFirecallItem((prev) => getItemClass({ ...prev.data(), [field]: value }));
};

const onChange =
Expand All @@ -52,24 +51,24 @@ export default function FirecallItemDialog({

const handleChange = (event: SelectChangeEvent) => {
setItemField('type', event.target.value);
setFirecallItem((prev) => ({
...firecallItemInfo(event.target.value).factory(),
...prev,
}));
// setFirecallItem((prev) => ({
// ...firecallItemInfo(event.target.value).factory(),
// ...prev,
// }));
};

return (
<>
<Dialog open={open} onClose={() => onClose()}>
<DialogTitle>
{item.id ? (
<>{itemInfo.name} bearbeiten</>
<>{item.markerName()} bearbeiten</>
) : (
<>Neu: {itemInfo.name} hinzufügen</>
<>Neu: {item.markerName()} hinzufügen</>
)}
</DialogTitle>
<DialogContent>
<DialogContentText>{itemInfo.dialogText(item)}</DialogContentText>
<DialogContentText>{item.dialogText()}</DialogContentText>
{allowTypeChange && (
<FormControl fullWidth variant="standard">
<InputLabel id="firecall-item-type-label">Element Typ</InputLabel>
Expand All @@ -80,19 +79,19 @@ export default function FirecallItemDialog({
label="Art"
onChange={handleChange}
>
{Object.entries(firecallItems)
.filter(([key, fcItem]) => key !== 'fallback')
.map(([key, fcItem]) => (
{Object.entries(fcItemNames)
.filter(([key, name]) => key !== 'fallback')
.map(([key, name]) => (
<MenuItem key={key} value={key}>
{fcItem.name}
{name}
</MenuItem>
))}
</Select>
</FormControl>
)}
{Object.entries(itemInfo.fields).map(([key, label]) => (
{Object.entries(item.fields()).map(([key, label]) => (
<React.Fragment key={key}>
{itemInfo.dateFields.includes(key) && (
{item.dateFields().includes(key) && (
<MyDateTimePicker
label={label}
value={
Expand All @@ -105,21 +104,38 @@ export default function FirecallItemDialog({
}}
/>
)}
{!itemInfo.dateFields.includes(key) && (
<TextField
margin="dense"
id={key}
key={key}
label={label}
type={
(itemInfo.fieldTypes && itemInfo.fieldTypes[key]) || 'text'
}
fullWidth
variant="standard"
onChange={onChange(key)}
value={((item as any)[key] as string) || ''}
/>
{item.fieldTypes()[key] === 'boolean' && (
<FormGroup>
<FormControlLabel
control={
<Checkbox
checked={
(item as any)[key] === 'true' ||
(item as any)[key] === true
}
onChange={(event, checked) => {
setItemField(key, checked ? 'true' : 'false');
}}
/>
}
label={label}
/>
</FormGroup>
)}
{!item.dateFields().includes(key) &&
item.fieldTypes()[key] !== 'boolean' && (
<TextField
margin="dense"
id={key}
key={key}
label={label}
type={item.fieldTypes()[key] || 'text'}
fullWidth
variant="standard"
onChange={onChange(key)}
value={((item as any)[key] as string) || ''}
/>
)}
</React.Fragment>
))}
</DialogContent>
Expand All @@ -146,7 +162,7 @@ export default function FirecallItemDialog({
color="primary"
onClick={() => {
setOpen(false);
onClose(item);
onClose(item.filteredData());
}}
>
{item.id ? 'Aktualisieren' : 'Hinzufügen'}
Expand All @@ -155,10 +171,8 @@ export default function FirecallItemDialog({
</Dialog>
{confirmDelete && (
<ConfirmDialog
title={`${itemInfo.name} ${itemInfo.title(item)} löschen`}
text={`Element ${itemInfo.name} ${itemInfo.title(
item
)} wirklich löschen?`}
title={`${item.title()} löschen`}
text={`${item.title()} wirklich löschen?`}
onConfirm={(result) => {
setConfirmDelete(false);
if (result) {
Expand Down
112 changes: 112 additions & 0 deletions src/components/FirecallItems/elements/CircleMarker.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import L, { Icon, IconOptions } from 'leaflet';
import { ReactNode } from 'react';
import { Circle as LeafletCircle } from 'react-leaflet';
import { Circle, FirecallItem } from '../../firebase/firestore';
import { circleIcon } from '../icons';
import { FirecallItemBase } from './FirecallItemBase';

export class CircleMarker extends FirecallItemBase {
color: string;
radius: number;
opacity: number;
fill: string;

public constructor(firecallItem?: Circle) {
super(firecallItem);
this.color = firecallItem?.color || 'green';
this.radius = firecallItem?.radius || 50;
this.opacity = firecallItem?.opacity || 100;
this.fill = firecallItem?.fill === undefined ? 'true' : firecallItem.fill;
}

public data(): FirecallItem {
return {
...super.data(),
color: this.color,
radius: this.radius,
opacity: this.opacity,
fill: this.fill,
} as Circle;
}

public markerName(): string {
return `Kreis`;
}

public title(): string {
return `${this.markerName()} ${this.name}`;
}
public info(): string {
return `Radius: ${this.radius || 0}m`;
}

public body(): string {
return `${this.lat},${this.lng}\nUmkreis: ${this.radius || 0}m`;
}

public dialogText(): ReactNode {
return (
<>
Um die Kreis zu zeichnen, auf die gewünschten Positionen klicken. <br />
{this.name || ''}
</>
);
}

public fields(): { [fieldName: string]: string } {
return {
...super.fields(),
radius: 'Radius (m)',
color: 'Farbe (HTML bzw. Englisch)',
fill: 'Kreis ausfüllen',
opacity: 'Deckkraft (in Prozent)',
};
}

public dateFields(): string[] {
return [];
}

public fieldTypes(): { [fieldName: string]: string } {
return {
fill: 'boolean',
};
}
public popupFn(): ReactNode {
return (
<>
<b>Kreis {this.name}</b>
<br />
{this.radius || 0}m
</>
);
}
public titleFn(): string {
return `Kreis ${this.name}: Radius ${this.radius || 0}m`;
}
public icon(): Icon<IconOptions> {
return circleIcon;
}

public static factory(): FirecallItemBase {
return new CircleMarker();
}

public renderMarker(selectItem: (item: FirecallItem) => void) {
return (
<>
{super.renderMarker(selectItem)}
<LeafletCircle
key={'circle' + this.id}
color={this.color}
radius={this.radius}
center={L.latLng(this.lat, this.lng)}
opacity={this.opacity / 100}
fill={this.fill === 'true'}
>
{this.renderPopup(selectItem)}
</LeafletCircle>
</>
);
}
}
Loading

0 comments on commit de97515

Please sign in to comment.