-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge remote-tracking branch 'origin/main' into production
- Loading branch information
Showing
22 changed files
with
534 additions
and
38 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
import {useState} from "react"; | ||
import {useOnce} from "../hooks/use-once"; | ||
import {User, usersFromJson } from "zero-zummon" | ||
|
||
type UseUserReturn = { | ||
loadingUsers: boolean, | ||
users: User[], | ||
changeUser: (newUser: User) => void, | ||
removeUser: (userId: string) => void, | ||
} | ||
|
||
export const useUsers = (): UseUserReturn => { | ||
const [loadingUsers, setLoading] = useState(true) | ||
const [users, setUsers] = useState<User[]>([]) | ||
|
||
const changeUser = (newUser: User) => { | ||
setUsers(users.map(user => user.id.toString() === newUser.id.toString() ? newUser : user)) | ||
} | ||
|
||
useOnce(async () => { | ||
try { | ||
const response = await fetch(import.meta.env.VITE_ZTOR_URL + '/users', { | ||
credentials: 'include', | ||
}) | ||
if (response.status === 401) { | ||
redirectToLogin() | ||
return | ||
} | ||
if (!response.ok) { | ||
throw new Error(`Could not load users: ${response.status} ${response.statusText}`) | ||
} | ||
|
||
setUsers(usersFromJson(await response.text())) | ||
} catch (error) { | ||
alert((error as Error).message) | ||
} finally { | ||
setLoading(false) | ||
} | ||
}) | ||
|
||
const removeUser = (userId: any) => { | ||
setUsers(users.filter(user => user.id.toString() !== userId.toString())) | ||
} | ||
|
||
return { | ||
loadingUsers, | ||
users, | ||
changeUser, | ||
removeUser, | ||
} | ||
} | ||
|
||
export const redirectToLogin = () => { | ||
window.location.href = import.meta.env.VITE_ZTOR_URL + '/login?redirectUrl=' + encodeURIComponent(window.location.href) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
import React, { FormEvent, FunctionComponent, useEffect, useState } from "react"; | ||
import { useParams, useNavigate } from "react-router-dom"; | ||
import { PrimeReactProvider } from "primereact/api"; | ||
import { InputText } from "primereact/inputtext"; | ||
import { Button } from "primereact/button"; | ||
import { User } from "zero-zummon"; | ||
import { redirectToLogin } from "./use-users"; | ||
|
||
export const UserForm: FunctionComponent = () => { | ||
const {userId} = useParams<{ userId: string }>(); | ||
const [user, setUser] = useState<User | null>(null); | ||
const [originalData, setOriginalData] = useState<User | null>(null); | ||
|
||
const [loading, setLoading] = useState(false); | ||
const [isEditing, setIsEditing] = useState(false); | ||
const navigate = useNavigate(); | ||
|
||
const handleCancel = () => { | ||
if (originalData) { | ||
setUser(originalData); // Revert to original data | ||
} | ||
setIsEditing(false); | ||
}; | ||
|
||
const handleEditToggle = () => { | ||
setIsEditing(true); | ||
}; | ||
|
||
const handleInputChange =(e: React.ChangeEvent<HTMLInputElement>) => { | ||
const { name, value, type, checked } = e.target; | ||
setUser((prev) => ({...prev, | ||
[name]: type === "checkbox" ? checked : value, | ||
} as User)); | ||
}; | ||
|
||
useEffect(() => { | ||
if (userId) { | ||
const fetchUser = async () => { | ||
setLoading(true); | ||
try { | ||
const response = await fetch(`${import.meta.env.VITE_ZTOR_URL}/users/${userId}`, { | ||
credentials: "include", | ||
}); | ||
if (response.status === 401) { | ||
redirectToLogin(); | ||
return; | ||
} | ||
if (response.ok) { | ||
const userData = await response.json(); | ||
setUser(userData); | ||
setOriginalData(userData); | ||
} else { | ||
alert(`Error fetching user: ${response.statusText}`); | ||
} | ||
} catch (error) { | ||
alert((error as Error).message); | ||
} finally { | ||
setLoading(false); | ||
} | ||
}; | ||
fetchUser(); | ||
} else { | ||
setIsEditing(true); | ||
} | ||
}, [userId]); | ||
|
||
const handleSubmit = async (event: FormEvent) => { | ||
event.preventDefault(); | ||
setLoading(true); | ||
try { | ||
const method = userId ? "PUT" : "POST"; | ||
const url = `${import.meta.env.VITE_ZTOR_URL}/users` | ||
const response = await fetch(url, { | ||
method, | ||
headers: { | ||
"Content-Type": "application/json", | ||
}, | ||
credentials: "include", | ||
body: JSON.stringify(user), | ||
}); | ||
if (response.status === 401) { | ||
redirectToLogin(); | ||
return; | ||
} | ||
if (!response.ok) { | ||
alert(`Error: ${response.statusText}`); | ||
} | ||
|
||
navigate(`/users`); | ||
} finally { | ||
setIsEditing(false); | ||
setLoading(false); | ||
} | ||
}; | ||
|
||
return ( | ||
<PrimeReactProvider> | ||
<div style={{ padding: "20px", maxWidth: "500px", margin: "0 auto" }}> | ||
<h3>{userId ? "Edit User" : "Add User"}</h3> | ||
<form | ||
onSubmit={handleSubmit} | ||
style={{ display: "flex", flexDirection: "column", gap: "10px" }} | ||
> | ||
<label htmlFor="name">Keycloak ID:</label> | ||
<InputText | ||
id="id" | ||
name="id" | ||
value={user?.id || ""} | ||
onChange={handleInputChange} | ||
disabled={!isEditing} | ||
/> | ||
<label htmlFor="name">Note:</label> | ||
<InputText | ||
id="note" | ||
name="note" | ||
value={user?.note || ""} | ||
onChange={handleInputChange} | ||
disabled={!isEditing} | ||
/> | ||
<div> | ||
<label htmlFor="isAdmin" style={{ display: "flex", alignItems: "center", gap: "8px" }}> | ||
<input | ||
type="checkbox" | ||
id="isAdmin" | ||
name="isAdmin" | ||
checked={user?.isAdmin || false} | ||
onChange={handleInputChange} | ||
disabled={!isEditing} | ||
/> | ||
Admin | ||
</label> | ||
</div> | ||
|
||
<div style={{ display: "flex", justifyContent: "space-between", marginTop: "10px" }}> | ||
{isEditing ? ( | ||
<> | ||
<Button label="Cancel" onClick={handleCancel} type="button" disabled={loading} /> | ||
<Button label={loading ? "Saving..." : "Save"} type="submit" disabled={loading} /> | ||
</> | ||
) : ( | ||
<Button label="Edit" onClick={handleEditToggle} type="button" disabled={loading} /> | ||
)} | ||
</div> | ||
</form> | ||
</div> | ||
</PrimeReactProvider> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
import React, {FunctionComponent} from "react"; | ||
import { DataTable } from 'primereact/datatable'; | ||
import { Column } from 'primereact/column'; | ||
import {useUsers} from "./use-users"; | ||
import {PrimeReactProvider} from "primereact/api"; | ||
import {User} from "zero-zummon" | ||
|
||
import "primereact/resources/themes/lara-light-cyan/theme.css" | ||
import 'primeicons/primeicons.css' | ||
import {DeleteButton} from "./delete-button"; | ||
import {EditButton} from "./edit-button"; | ||
import {Button} from "primereact/button"; | ||
import {useNavigate} from "react-router-dom" | ||
|
||
export const Users: FunctionComponent = () => { | ||
const {loadingUsers, users, changeUser, removeUser} = useUsers() | ||
const navigate = useNavigate(); | ||
|
||
return ( | ||
<PrimeReactProvider> | ||
<div css={{ | ||
display: 'flex', | ||
justifyContent: 'space-between', | ||
alignItems: 'center', | ||
padding: '1em 1em', | ||
boxShadow: '1px solid #ddd' | ||
}}> | ||
<h3>Users List</h3> | ||
<Button | ||
label="Nieuw" | ||
icon="pi pi-pencil" | ||
onClick={(event) => navigate(`/users/new-user`)} | ||
/> | ||
</div> | ||
<DataTable | ||
value={users} | ||
loading={loadingUsers} | ||
sortField="created" | ||
sortOrder={-1} | ||
filterDisplay="row" | ||
> | ||
<Column field="note" header="Note" sortable filter/> | ||
<Column | ||
field="isAdmin" | ||
header="Admin" | ||
body={(user: User) => ( | ||
<div style={{ textAlign: 'center' }}> | ||
{user.isAdmin ? ( | ||
<span style={{ color: 'green' }}>✔</span> | ||
) : ( | ||
<span style={{ color: 'red' }}>✘</span> | ||
)} | ||
</div> | ||
)} | ||
/> | ||
<Column body={(user: User) => ( | ||
<div css={{ | ||
display: 'flex', | ||
'> *': { | ||
margin: `${1 / 6}rem` | ||
}, | ||
}}> | ||
<DeleteButton type="users" id={user.id} onDelete={removeUser}/> | ||
<EditButton type="users" id={user.id}/> | ||
</div> | ||
)}/> | ||
</DataTable> | ||
</PrimeReactProvider> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
29 changes: 29 additions & 0 deletions
29
frontend/src/components/company-survey-v2/time-series/interval-dropdown.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import {FunctionComponent} from "react" | ||
import {Dropdown} from "primereact/dropdown" | ||
import {isoStringToDateTimeUnit, dateTimeUnitToIsoString} from "zero-zummon" | ||
import {SelectItem, SelectItemOptionsType} from "primereact/selectitem" | ||
|
||
const options: SelectItemOptionsType = [ | ||
{ | ||
value: "PT15M", | ||
label: "Kwartier", | ||
}, | ||
{ | ||
value: "P1D", | ||
label: "Dag", | ||
}, | ||
{ | ||
value: "P1M", | ||
label: "Maand", | ||
}, | ||
] | ||
|
||
export const IntervalDropdown: FunctionComponent<{ | ||
timeStep: any, | ||
setTimeStep: (timeStep: any) => void | ||
}> = ({timeStep, setTimeStep}) => { | ||
|
||
return ( | ||
<Dropdown options={options} value={dateTimeUnitToIsoString(timeStep)} onChange={event => setTimeStep(isoStringToDateTimeUnit(event.value))}/> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.