mirror of
https://gitlab.dit.htwk-leipzig.de/htwk-software/htwkalender-pwa.git
synced 2025-07-23 04:58:50 +02:00
Merge branch 'refs/heads/main' into 3-semi-offline-room-finder
# Conflicts: # frontend/src/components/RoomOccupationOffline.vue # frontend/src/model/roomOccupancyList.ts
This commit is contained in:
8
.gitattributes
vendored
Normal file
8
.gitattributes
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
*.ts text eol=lf
|
||||||
|
*.go text eol=lf
|
||||||
|
*.js text eol=lf
|
||||||
|
*.json text eol=lf
|
||||||
|
*.vue text eol=lf
|
||||||
|
*.md text eol=lf
|
||||||
|
*.css text eol=lf
|
||||||
|
*.scss text eol=lf
|
@ -4,13 +4,19 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>HTWKalender</title>
|
<title>HTWKalender</title>
|
||||||
<meta name="description" content="Calendar implementation for the HTWK Leipzig timetable. Evaluation and display of the individual dates in iCal format.">
|
<meta
|
||||||
<meta name="keywords" content="HTWK, calendar, iCal, dates, events, schedule">
|
name="description"
|
||||||
|
content="Calendar implementation for the HTWK Leipzig timetable. Evaluation and display of the individual dates in iCal format."
|
||||||
|
/>
|
||||||
|
<meta
|
||||||
|
name="keywords"
|
||||||
|
content="HTWK, calendar, iCal, dates, events, schedule"
|
||||||
|
/>
|
||||||
<link rel="icon" href="/favicon.ico" sizes="32x32" />
|
<link rel="icon" href="/favicon.ico" sizes="32x32" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/htwk.svg" />
|
<link rel="icon" type="image/svg+xml" href="/htwk.svg" />
|
||||||
<link rel="mask-icon" href="/htwk-mask.svg" color="#00494c" />
|
<link rel="mask-icon" href="/htwk-mask.svg" color="#00494c" />
|
||||||
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
|
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
|
||||||
<meta name="theme-color" content="#1b2022">
|
<meta name="theme-color" content="#1b2022" />
|
||||||
<link
|
<link
|
||||||
id="theme-link"
|
id="theme-link"
|
||||||
rel="stylesheet"
|
rel="stylesheet"
|
||||||
|
2
frontend/package-lock.json
generated
2
frontend/package-lock.json
generated
@ -4477,6 +4477,7 @@
|
|||||||
"version": "3.6.0",
|
"version": "3.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz",
|
||||||
"integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==",
|
"integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==",
|
||||||
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "github",
|
"type": "github",
|
||||||
"url": "https://github.com/sponsors/kossnocorp"
|
"url": "https://github.com/sponsors/kossnocorp"
|
||||||
@ -4486,6 +4487,7 @@
|
|||||||
"version": "3.1.3",
|
"version": "3.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/date-fns-tz/-/date-fns-tz-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/date-fns-tz/-/date-fns-tz-3.1.3.tgz",
|
||||||
"integrity": "sha512-ZfbMu+nbzW0mEzC8VZrLiSWvUIaI3aRHeq33mTe7Y38UctKukgqPR4nTDwcwS4d64Gf8GghnVsroBuMY3eiTeA==",
|
"integrity": "sha512-ZfbMu+nbzW0mEzC8VZrLiSWvUIaI3aRHeq33mTe7Y38UctKukgqPR4nTDwcwS4d64Gf8GghnVsroBuMY3eiTeA==",
|
||||||
|
"license": "MIT",
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"date-fns": "^3.0.0"
|
"date-fns": "^3.0.0"
|
||||||
}
|
}
|
||||||
|
BIN
frontend/public/1280x720.png
Normal file
BIN
frontend/public/1280x720.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 36 KiB |
BIN
frontend/public/390x844.png
Normal file
BIN
frontend/public/390x844.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 112 KiB |
@ -3,15 +3,14 @@
|
|||||||
@each $name, $color in $colors {
|
@each $name, $color in $colors {
|
||||||
@for $i from 0 through 5 {
|
@for $i from 0 through 5 {
|
||||||
@if ($i == 0) {
|
@if ($i == 0) {
|
||||||
--#{$name}-50:#{tint($color, (5 - $i) * 19%)};
|
--#{$name}-50: #{tint($color, (5 - $i) * 19%)};
|
||||||
}
|
} @else {
|
||||||
@else {
|
--#{$name}-#{$i * 100}: #{tint($color, (5 - $i) * 19%)};
|
||||||
--#{$name}-#{$i * 100}:#{tint($color, (5 - $i) * 19%)};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@for $i from 1 through 4 {
|
@for $i from 1 through 4 {
|
||||||
--#{$name}-#{($i + 5) * 100}:#{shade($color, $i * 15%)};
|
--#{$name}-#{($i + 5) * 100}: #{shade($color, $i * 15%)};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
// core
|
// core
|
||||||
.p-component, .p-component * {
|
.p-component,
|
||||||
|
.p-component * {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -17,7 +18,8 @@
|
|||||||
list-style: none;
|
list-style: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-disabled, .p-disabled * {
|
.p-disabled,
|
||||||
|
.p-disabled * {
|
||||||
cursor: default;
|
cursor: default;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
@ -66,7 +68,9 @@
|
|||||||
.p-connected-overlay {
|
.p-connected-overlay {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: scaleY(0.8);
|
transform: scaleY(0.8);
|
||||||
transition: transform .12s cubic-bezier(0, 0, 0.2, 1), opacity .12s cubic-bezier(0, 0, 0.2, 1);
|
transition:
|
||||||
|
transform 0.12s cubic-bezier(0, 0, 0.2, 1),
|
||||||
|
opacity 0.12s cubic-bezier(0, 0, 0.2, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-connected-overlay-visible {
|
.p-connected-overlay-visible {
|
||||||
@ -77,7 +81,7 @@
|
|||||||
.p-connected-overlay-hidden {
|
.p-connected-overlay-hidden {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: scaleY(1);
|
transform: scaleY(1);
|
||||||
transition: opacity .1s linear;
|
transition: opacity 0.1s linear;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Vue based overlay animations */
|
/* Vue based overlay animations */
|
||||||
@ -91,11 +95,13 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.p-connected-overlay-enter-active {
|
.p-connected-overlay-enter-active {
|
||||||
transition: transform .12s cubic-bezier(0, 0, 0.2, 1), opacity .12s cubic-bezier(0, 0, 0.2, 1);
|
transition:
|
||||||
|
transform 0.12s cubic-bezier(0, 0, 0.2, 1),
|
||||||
|
opacity 0.12s cubic-bezier(0, 0, 0.2, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-connected-overlay-leave-active {
|
.p-connected-overlay-leave-active {
|
||||||
transition: opacity .1s linear;
|
transition: opacity 0.1s linear;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Toggleable Content */
|
/* Toggleable Content */
|
||||||
@ -136,7 +142,8 @@
|
|||||||
transition-duration: $transitionDuration;
|
transition-duration: $transitionDuration;
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-disabled, .p-component:disabled {
|
.p-disabled,
|
||||||
|
.p-component:disabled {
|
||||||
opacity: $disabledOpacity;
|
opacity: $disabledOpacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,7 +79,8 @@
|
|||||||
color: $menuitemTextActiveColor;
|
color: $menuitemTextActiveColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-menuitem-icon, .p-submenu-icon {
|
.p-menuitem-icon,
|
||||||
|
.p-submenu-icon {
|
||||||
color: $menuitemIconActiveColor;
|
color: $menuitemIconActiveColor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -103,7 +104,8 @@
|
|||||||
color: $menuitemTextHoverColor;
|
color: $menuitemTextHoverColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-menuitem-icon, .p-submenu-icon {
|
.p-menuitem-icon,
|
||||||
|
.p-submenu-icon {
|
||||||
color: $menuitemTextHoverColor;
|
color: $menuitemTextHoverColor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -117,7 +119,8 @@
|
|||||||
color: $menuitemTextHoverColor;
|
color: $menuitemTextHoverColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-menuitem-icon, .p-submenu-icon {
|
.p-menuitem-icon,
|
||||||
|
.p-submenu-icon {
|
||||||
color: $menuitemIconHoverColor;
|
color: $menuitemIconHoverColor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -135,7 +138,8 @@
|
|||||||
color: $menuitemTextHoverColor;
|
color: $menuitemTextHoverColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-menuitem-icon, .p-submenu-icon {
|
.p-menuitem-icon,
|
||||||
|
.p-submenu-icon {
|
||||||
color: $menuitemIconHoverColor;
|
color: $menuitemIconHoverColor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -181,7 +185,8 @@
|
|||||||
color: $horizontalMenuRootMenuitemTextHoverColor;
|
color: $horizontalMenuRootMenuitemTextHoverColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-menuitem-icon, .p-submenu-icon {
|
.p-menuitem-icon,
|
||||||
|
.p-submenu-icon {
|
||||||
color: $horizontalMenuRootMenuitemIconHoverColor;
|
color: $horizontalMenuRootMenuitemIconHoverColor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -192,16 +197,16 @@
|
|||||||
|
|
||||||
@mixin placeholder {
|
@mixin placeholder {
|
||||||
::-webkit-input-placeholder {
|
::-webkit-input-placeholder {
|
||||||
@content
|
@content;
|
||||||
}
|
}
|
||||||
:-moz-placeholder {
|
:-moz-placeholder {
|
||||||
@content
|
@content;
|
||||||
}
|
}
|
||||||
::-moz-placeholder {
|
::-moz-placeholder {
|
||||||
@content
|
@content;
|
||||||
}
|
}
|
||||||
:-ms-input-placeholder {
|
:-ms-input-placeholder {
|
||||||
@content
|
@content;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -220,7 +225,6 @@
|
|||||||
.p-menuitem-link {
|
.p-menuitem-link {
|
||||||
padding-left: $val * ($index + 1);
|
padding-left: $val * ($index + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@if $index < $length {
|
@if $index < $length {
|
||||||
@include nested-submenu-indents($val, $index + 2, $length);
|
@include nested-submenu-indents($val, $index + 2, $length);
|
||||||
|
@ -44,7 +44,8 @@
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-button-group .p-button:not(:last-child), .p-button-group .p-button:not(:last-child):hover {
|
.p-button-group .p-button:not(:last-child),
|
||||||
|
.p-button-group .p-button:not(:last-child):hover {
|
||||||
border-right: 0 none;
|
border-right: 0 none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -258,7 +259,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-button.p-button-secondary, .p-button-group.p-button-secondary > .p-button, .p-splitbutton.p-button-secondary > .p-button {
|
.p-button.p-button-secondary,
|
||||||
|
.p-button-group.p-button-secondary > .p-button,
|
||||||
|
.p-splitbutton.p-button-secondary > .p-button {
|
||||||
color: $secondaryButtonTextColor;
|
color: $secondaryButtonTextColor;
|
||||||
background: $secondaryButtonBg;
|
background: $secondaryButtonBg;
|
||||||
border: $secondaryButtonBorder;
|
border: $secondaryButtonBorder;
|
||||||
@ -316,7 +319,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-button.p-button-info, .p-button-group.p-button-info > .p-button, .p-splitbutton.p-button-info > .p-button {
|
.p-button.p-button-info,
|
||||||
|
.p-button-group.p-button-info > .p-button,
|
||||||
|
.p-splitbutton.p-button-info > .p-button {
|
||||||
color: $infoButtonTextColor;
|
color: $infoButtonTextColor;
|
||||||
background: $infoButtonBg;
|
background: $infoButtonBg;
|
||||||
border: $infoButtonBorder;
|
border: $infoButtonBorder;
|
||||||
@ -374,7 +379,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-button.p-button-success, .p-button-group.p-button-success > .p-button, .p-splitbutton.p-button-success > .p-button {
|
.p-button.p-button-success,
|
||||||
|
.p-button-group.p-button-success > .p-button,
|
||||||
|
.p-splitbutton.p-button-success > .p-button {
|
||||||
color: $successButtonTextColor;
|
color: $successButtonTextColor;
|
||||||
background: $successButtonBg;
|
background: $successButtonBg;
|
||||||
border: $successButtonBorder;
|
border: $successButtonBorder;
|
||||||
@ -432,7 +439,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-button.p-button-warning, .p-button-group.p-button-warning > .p-button, .p-splitbutton.p-button-warning > .p-button {
|
.p-button.p-button-warning,
|
||||||
|
.p-button-group.p-button-warning > .p-button,
|
||||||
|
.p-splitbutton.p-button-warning > .p-button {
|
||||||
color: $warningButtonTextColor;
|
color: $warningButtonTextColor;
|
||||||
background: $warningButtonBg;
|
background: $warningButtonBg;
|
||||||
border: $warningButtonBorder;
|
border: $warningButtonBorder;
|
||||||
@ -490,7 +499,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-button.p-button-help, .p-button-group.p-button-help > .p-button, .p-splitbutton.p-button-help > .p-button {
|
.p-button.p-button-help,
|
||||||
|
.p-button-group.p-button-help > .p-button,
|
||||||
|
.p-splitbutton.p-button-help > .p-button {
|
||||||
color: $helpButtonTextColor;
|
color: $helpButtonTextColor;
|
||||||
background: $helpButtonBg;
|
background: $helpButtonBg;
|
||||||
border: $helpButtonBorder;
|
border: $helpButtonBorder;
|
||||||
@ -548,7 +559,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-button.p-button-danger, .p-button-group.p-button-danger > .p-button, .p-splitbutton.p-button-danger > .p-button {
|
.p-button.p-button-danger,
|
||||||
|
.p-button-group.p-button-danger > .p-button,
|
||||||
|
.p-splitbutton.p-button-danger > .p-button {
|
||||||
color: $dangerButtonTextColor;
|
color: $dangerButtonTextColor;
|
||||||
background: $dangerButtonBg;
|
background: $dangerButtonBg;
|
||||||
border: $dangerButtonBorder;
|
border: $dangerButtonBorder;
|
||||||
@ -607,7 +620,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@if variable-exists(contrastButtonTextColor) {
|
@if variable-exists(contrastButtonTextColor) {
|
||||||
.p-button.p-button-contrast, .p-button-group.p-button-contrast > .p-button, .p-splitbutton.p-button-contrast > .p-button {
|
.p-button.p-button-contrast,
|
||||||
|
.p-button-group.p-button-contrast > .p-button,
|
||||||
|
.p-splitbutton.p-button-contrast > .p-button {
|
||||||
color: $contrastButtonTextColor;
|
color: $contrastButtonTextColor;
|
||||||
background: $contrastButtonBg;
|
background: $contrastButtonBg;
|
||||||
border: $contrastButtonBorder;
|
border: $contrastButtonBorder;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
@use 'sass:math';
|
@use "sass:math";
|
||||||
|
|
||||||
// core
|
// core
|
||||||
.p-speeddial {
|
.p-speeddial {
|
||||||
@ -25,7 +25,9 @@
|
|||||||
.p-speeddial-item {
|
.p-speeddial-item {
|
||||||
transform: scale(0);
|
transform: scale(0);
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transition: transform 200ms cubic-bezier(0.4, 0, 0.2, 1) 0ms, opacity 0.8s;
|
transition:
|
||||||
|
transform 200ms cubic-bezier(0.4, 0, 0.2, 1) 0ms,
|
||||||
|
opacity 0.8s;
|
||||||
will-change: transform;
|
will-change: transform;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,7 +7,8 @@
|
|||||||
.p-splitbutton .p-splitbutton-defaultbutton,
|
.p-splitbutton .p-splitbutton-defaultbutton,
|
||||||
.p-splitbutton.p-button-rounded > .p-splitbutton-defaultbutton.p-button,
|
.p-splitbutton.p-button-rounded > .p-splitbutton-defaultbutton.p-button,
|
||||||
.p-splitbutton.p-button-outlined > .p-splitbutton-defaultbutton.p-button,
|
.p-splitbutton.p-button-outlined > .p-splitbutton-defaultbutton.p-button,
|
||||||
.p-splitbutton.p-button-outlined > .p-splitbutton-defaultbutton.p-button-outlined.p-button:hover {
|
.p-splitbutton.p-button-outlined
|
||||||
|
> .p-splitbutton-defaultbutton.p-button-outlined.p-button:hover {
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
border-top-right-radius: 0;
|
border-top-right-radius: 0;
|
||||||
border-bottom-right-radius: 0;
|
border-bottom-right-radius: 0;
|
||||||
|
@ -84,12 +84,19 @@
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-datatable-resizable-table > .p-datatable-thead > tr > th.p-resizable-column:not(.p-frozen-column) {
|
.p-datatable-resizable-table
|
||||||
|
> .p-datatable-thead
|
||||||
|
> tr
|
||||||
|
> th.p-resizable-column:not(.p-frozen-column) {
|
||||||
background-clip: padding-box;
|
background-clip: padding-box;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-datatable-resizable-table-fit > .p-datatable-thead > tr > th.p-resizable-column:last-child .p-column-resizer {
|
.p-datatable-resizable-table-fit
|
||||||
|
> .p-datatable-thead
|
||||||
|
> tr
|
||||||
|
> th.p-resizable-column:last-child
|
||||||
|
.p-column-resizer {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -399,7 +406,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&.p-datatable-scrollable > .p-datatable-wrapper > .p-datatable-table,
|
&.p-datatable-scrollable > .p-datatable-wrapper > .p-datatable-table,
|
||||||
&.p-datatable-scrollable > .p-datatable-wrapper > .p-virtualscroller > .p-datatable-table {
|
&.p-datatable-scrollable
|
||||||
|
> .p-datatable-wrapper
|
||||||
|
> .p-virtualscroller
|
||||||
|
> .p-datatable-table {
|
||||||
> .p-datatable-thead,
|
> .p-datatable-thead,
|
||||||
> .p-datatable-tfoot {
|
> .p-datatable-tfoot {
|
||||||
background-color: $tableHeaderCellBg;
|
background-color: $tableHeaderCellBg;
|
||||||
|
@ -36,7 +36,6 @@
|
|||||||
&:focus-visible {
|
&:focus-visible {
|
||||||
@include focused();
|
@include focused();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-column-filter-clear-button {
|
.p-column-filter-clear-button {
|
||||||
|
@ -80,7 +80,9 @@
|
|||||||
border: $inputListItemBorder;
|
border: $inputListItemBorder;
|
||||||
color: $inputListItemTextColor;
|
color: $inputListItemTextColor;
|
||||||
background: $inputListItemBg;
|
background: $inputListItemBg;
|
||||||
transition: transform $transitionDuration, $listItemTransition;
|
transition:
|
||||||
|
transform $transitionDuration,
|
||||||
|
$listItemTransition;
|
||||||
|
|
||||||
&:first-child {
|
&:first-child {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
|
@ -76,7 +76,9 @@
|
|||||||
border: $inputListItemBorder;
|
border: $inputListItemBorder;
|
||||||
color: $inputListItemTextColor;
|
color: $inputListItemTextColor;
|
||||||
background: $inputListItemBg;
|
background: $inputListItemBg;
|
||||||
transition: transform $transitionDuration, $listItemTransition;
|
transition:
|
||||||
|
transform $transitionDuration,
|
||||||
|
$listItemTransition;
|
||||||
|
|
||||||
&:first-child {
|
&:first-child {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
|
@ -29,19 +29,27 @@
|
|||||||
flex-direction: row-reverse;
|
flex-direction: row-reverse;
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-timeline-vertical.p-timeline-alternate .p-timeline-event:nth-child(odd) .p-timeline-event-opposite {
|
.p-timeline-vertical.p-timeline-alternate
|
||||||
|
.p-timeline-event:nth-child(odd)
|
||||||
|
.p-timeline-event-opposite {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-timeline-vertical.p-timeline-alternate .p-timeline-event:nth-child(odd) .p-timeline-event-content {
|
.p-timeline-vertical.p-timeline-alternate
|
||||||
|
.p-timeline-event:nth-child(odd)
|
||||||
|
.p-timeline-event-content {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-timeline-vertical.p-timeline-alternate .p-timeline-event:nth-child(even) .p-timeline-event-opposite {
|
.p-timeline-vertical.p-timeline-alternate
|
||||||
|
.p-timeline-event:nth-child(even)
|
||||||
|
.p-timeline-event-opposite {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-timeline-vertical.p-timeline-alternate .p-timeline-event:nth-child(even) .p-timeline-event-content {
|
.p-timeline-vertical.p-timeline-alternate
|
||||||
|
.p-timeline-event:nth-child(even)
|
||||||
|
.p-timeline-event-content {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -146,6 +154,3 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -150,7 +150,14 @@
|
|||||||
.p-treetable-scrollable-both .p-treetable-thead > tr > th,
|
.p-treetable-scrollable-both .p-treetable-thead > tr > th,
|
||||||
.p-treetable-scrollable-both .p-treetable-tbody > tr > td,
|
.p-treetable-scrollable-both .p-treetable-tbody > tr > td,
|
||||||
.p-treetable-scrollable-both .p-treetable-tfoot > tr > td,
|
.p-treetable-scrollable-both .p-treetable-tfoot > tr > td,
|
||||||
.p-treetable-scrollable-horizontal .p-treetable-thead > tr > th .p-treetable-scrollable-horizontal .p-treetable-tbody > tr > td,
|
.p-treetable-scrollable-horizontal
|
||||||
|
.p-treetable-thead
|
||||||
|
> tr
|
||||||
|
> th
|
||||||
|
.p-treetable-scrollable-horizontal
|
||||||
|
.p-treetable-tbody
|
||||||
|
> tr
|
||||||
|
> td,
|
||||||
.p-treetable-scrollable-horizontal .p-treetable-tfoot > tr > td {
|
.p-treetable-scrollable-horizontal .p-treetable-tfoot > tr > td {
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
}
|
}
|
||||||
@ -299,7 +306,6 @@
|
|||||||
&:hover {
|
&:hover {
|
||||||
color: $highlightTextColor;
|
color: $highlightTextColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,8 +20,8 @@
|
|||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-fileupload > input[type='file'],
|
.p-fileupload > input[type="file"],
|
||||||
.p-fileupload-basic input[type='file'] {
|
.p-fileupload-basic input[type="file"] {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,7 +101,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.p-fileupload-row {
|
.p-fileupload-row {
|
||||||
>div {
|
> div {
|
||||||
padding: $tableBodyCellPadding;
|
padding: $tableBodyCellPadding;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,20 @@
|
|||||||
// core
|
// core
|
||||||
.p-colorpicker-panel .p-colorpicker-color {
|
.p-colorpicker-panel .p-colorpicker-color {
|
||||||
background: linear-gradient(to top, #000 0%, rgb(0 0 0 / 0) 100%), linear-gradient(to right, #fff 0%, rgb(255 255 255 / 0) 100%)
|
background: linear-gradient(to top, #000 0%, rgb(0 0 0 / 0) 100%),
|
||||||
|
linear-gradient(to right, #fff 0%, rgb(255 255 255 / 0) 100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-colorpicker-panel .p-colorpicker-hue {
|
.p-colorpicker-panel .p-colorpicker-hue {
|
||||||
background: linear-gradient(0deg, red 0, #ff0 17%, #0f0 33%, #0ff 50%, #00f 67%, #f0f 83%, red)
|
background: linear-gradient(
|
||||||
|
0deg,
|
||||||
|
red 0,
|
||||||
|
#ff0 17%,
|
||||||
|
#0f0 33%,
|
||||||
|
#0ff 50%,
|
||||||
|
#00f 67%,
|
||||||
|
#f0f 83%,
|
||||||
|
red
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// theme
|
// theme
|
||||||
|
@ -48,8 +48,8 @@
|
|||||||
|
|
||||||
.ql-picker-options {
|
.ql-picker-options {
|
||||||
background: $inputOverlayBg;
|
background: $inputOverlayBg;
|
||||||
border:$inputOverlayBorder;
|
border: $inputOverlayBorder;
|
||||||
box-shadow:$inputOverlayShadow;
|
box-shadow: $inputOverlayShadow;
|
||||||
border-radius: $borderRadius;
|
border-radius: $borderRadius;
|
||||||
padding: $inputListPadding;
|
padding: $inputListPadding;
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
margin-top: -.5rem;
|
margin-top: -0.5rem;
|
||||||
transition-property: all;
|
transition-property: all;
|
||||||
transition-timing-function: ease;
|
transition-timing-function: ease;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
@ -25,7 +25,7 @@
|
|||||||
.p-float-label:has(textarea.p-filled) label,
|
.p-float-label:has(textarea.p-filled) label,
|
||||||
.p-float-label:has(.p-inputwrapper-focus) label,
|
.p-float-label:has(.p-inputwrapper-focus) label,
|
||||||
.p-float-label:has(.p-inputwrapper-filled) label {
|
.p-float-label:has(.p-inputwrapper-filled) label {
|
||||||
top: -.75rem;
|
top: -0.75rem;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,5 +6,5 @@
|
|||||||
.p-icon-field > .p-input-icon {
|
.p-icon-field > .p-input-icon {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
margin-top: -.5rem;
|
margin-top: -0.5rem;
|
||||||
}
|
}
|
@ -11,7 +11,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.p-inputnumber-buttons-stacked .p-button.p-inputnumber-button .p-button-label,
|
.p-inputnumber-buttons-stacked .p-button.p-inputnumber-button .p-button-label,
|
||||||
.p-inputnumber-buttons-horizontal .p-button.p-inputnumber-button .p-button-label {
|
.p-inputnumber-buttons-horizontal
|
||||||
|
.p-button.p-inputnumber-button
|
||||||
|
.p-button-label {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,7 +41,9 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-inputnumber-buttons-stacked .p-inputnumber-button-group .p-button.p-inputnumber-button {
|
.p-inputnumber-buttons-stacked
|
||||||
|
.p-inputnumber-button-group
|
||||||
|
.p-button.p-inputnumber-button {
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
@use 'sass:math';
|
@use "sass:math";
|
||||||
|
|
||||||
// core
|
// core
|
||||||
.p-inputswitch {
|
.p-inputswitch {
|
||||||
@ -21,7 +21,7 @@
|
|||||||
|
|
||||||
.p-inputswitch-slider:before {
|
.p-inputswitch-slider:before {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
content: '';
|
content: "";
|
||||||
top: 50%;
|
top: 50%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,8 +175,7 @@
|
|||||||
&.p-multiselect {
|
&.p-multiselect {
|
||||||
&.p-multiselect-chip {
|
&.p-multiselect-chip {
|
||||||
.p-multiselect-label {
|
.p-multiselect-label {
|
||||||
padding: math.div(nth($inputPadding, 1), 2)
|
padding: math.div(nth($inputPadding, 1), 2) nth($inputPadding, 2);
|
||||||
nth($inputPadding, 2);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -275,4 +274,3 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,13 +19,13 @@
|
|||||||
.p-radiobutton-icon {
|
.p-radiobutton-icon {
|
||||||
-webkit-backface-visibility: hidden;
|
-webkit-backface-visibility: hidden;
|
||||||
backface-visibility: hidden;
|
backface-visibility: hidden;
|
||||||
transform: translateZ(0) scale(.1);
|
transform: translateZ(0) scale(0.1);
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-radiobutton.p-highlight .p-radiobutton-icon {
|
.p-radiobutton.p-highlight .p-radiobutton-icon {
|
||||||
transform: translateZ(0) scale(1.0, 1.0);
|
transform: translateZ(0) scale(1, 1);
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
@use 'sass:math';
|
@use "sass:math";
|
||||||
|
|
||||||
// core
|
// core
|
||||||
.p-rating {
|
.p-rating {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
@use 'sass:math';
|
@use "sass:math";
|
||||||
|
|
||||||
// core
|
// core
|
||||||
.p-slider {
|
.p-slider {
|
||||||
|
@ -116,8 +116,7 @@
|
|||||||
&.p-treeselect {
|
&.p-treeselect {
|
||||||
&.p-treeselect-chip {
|
&.p-treeselect-chip {
|
||||||
.p-treeselect-label {
|
.p-treeselect-label {
|
||||||
padding: math.div(nth($inputPadding, 1), 2)
|
padding: math.div(nth($inputPadding, 1), 2) nth($inputPadding, 2);
|
||||||
nth($inputPadding, 2);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -82,12 +82,20 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-megamenu-vertical:not(.p-megamenu-mobile) .p-megamenu-root-list > .p-menuitem-active > .p-megamenu-panel {
|
.p-megamenu-vertical:not(.p-megamenu-mobile)
|
||||||
|
.p-megamenu-root-list
|
||||||
|
> .p-menuitem-active
|
||||||
|
> .p-megamenu-panel {
|
||||||
left: 100%;
|
left: 100%;
|
||||||
top: 0;
|
top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-megamenu-vertical .p-megamenu-root-list > .p-menuitem > .p-menuitem-content > .p-menuitem-link > .p-submenu-icon {
|
.p-megamenu-vertical
|
||||||
|
.p-megamenu-root-list
|
||||||
|
> .p-menuitem
|
||||||
|
> .p-menuitem-content
|
||||||
|
> .p-menuitem-link
|
||||||
|
> .p-submenu-icon {
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -266,7 +274,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
&.p-menuitem-active {
|
&.p-menuitem-active {
|
||||||
> .p-menuitem-content {
|
> .p-menuitem-content {
|
||||||
> .p-menuitem-link {
|
> .p-menuitem-link {
|
||||||
|
@ -54,7 +54,12 @@
|
|||||||
top: 0;
|
top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-menubar .p-submenu-list .p-menuitem .p-menuitem-content .p-menuitem-link .p-submenu-icon {
|
.p-menubar
|
||||||
|
.p-submenu-list
|
||||||
|
.p-menuitem
|
||||||
|
.p-menuitem-content
|
||||||
|
.p-menuitem-link
|
||||||
|
.p-submenu-icon {
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,7 +192,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
&.p-menuitem-active {
|
&.p-menuitem-active {
|
||||||
> .p-menuitem-content {
|
> .p-menuitem-content {
|
||||||
> .p-menuitem-link {
|
> .p-menuitem-link {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
@use 'sass:math';
|
@use "sass:math";
|
||||||
|
|
||||||
// core
|
// core
|
||||||
.p-steps {
|
.p-steps {
|
||||||
@ -99,7 +99,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&:before {
|
&:before {
|
||||||
content:' ';
|
content: " ";
|
||||||
border-top: $divider;
|
border-top: $divider;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
|
@ -41,7 +41,10 @@
|
|||||||
|
|
||||||
.p-message-leave-active {
|
.p-message-leave-active {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
transition: max-height 0.3s cubic-bezier(0, 1, 0, 1), opacity 0.3s, margin 0.15s;
|
transition:
|
||||||
|
max-height 0.3s cubic-bezier(0, 1, 0, 1),
|
||||||
|
opacity 0.3s,
|
||||||
|
margin 0.15s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-message-leave-active .p-message-close {
|
.p-message-leave-active .p-message-close {
|
||||||
|
@ -63,13 +63,23 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.p-toast-message-enter-active {
|
.p-toast-message-enter-active {
|
||||||
-webkit-transition: transform 0.3s, opacity 0.3s;
|
-webkit-transition:
|
||||||
transition: transform 0.3s, opacity 0.3s;
|
transform 0.3s,
|
||||||
|
opacity 0.3s;
|
||||||
|
transition:
|
||||||
|
transform 0.3s,
|
||||||
|
opacity 0.3s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-toast-message-leave-active {
|
.p-toast-message-leave-active {
|
||||||
-webkit-transition: max-height 0.45s cubic-bezier(0, 1, 0, 1), opacity 0.3s, margin-bottom 0.3s;
|
-webkit-transition:
|
||||||
transition: max-height 0.45s cubic-bezier(0, 1, 0, 1), opacity 0.3s, margin-bottom 0.3s;
|
max-height 0.45s cubic-bezier(0, 1, 0, 1),
|
||||||
|
opacity 0.3s,
|
||||||
|
margin-bottom 0.3s;
|
||||||
|
transition:
|
||||||
|
max-height 0.45s cubic-bezier(0, 1, 0, 1),
|
||||||
|
opacity 0.3s,
|
||||||
|
margin-bottom 0.3s;
|
||||||
}
|
}
|
||||||
|
|
||||||
// theme
|
// theme
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
display: inline-block;
|
display: inline-block;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 0 .5rem;
|
padding: 0 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-overlay-badge {
|
.p-overlay-badge {
|
||||||
@ -14,15 +14,15 @@
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
transform: translate(50%,-50%);
|
transform: translate(50%, -50%);
|
||||||
transform-origin: 100% 0;
|
transform-origin: 100% 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-badge.p-badge-dot {
|
.p-badge.p-badge-dot {
|
||||||
width: .5rem;
|
width: 0.5rem;
|
||||||
min-width: .5rem;
|
min-width: 0.5rem;
|
||||||
height: .5rem;
|
height: 0.5rem;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
@use 'sass:math';
|
@use "sass:math";
|
||||||
|
|
||||||
// core
|
// core
|
||||||
.p-chip {
|
.p-chip {
|
||||||
|
@ -35,4 +35,3 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,27 +25,31 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.p-progressbar-indeterminate .p-progressbar-value::before {
|
.p-progressbar-indeterminate .p-progressbar-value::before {
|
||||||
content: '';
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
background-color: inherit;
|
background-color: inherit;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
will-change: left, right;
|
will-change: left, right;
|
||||||
-webkit-animation: p-progressbar-indeterminate-anim 2.1s cubic-bezier(0.65, 0.815, 0.735, 0.395) infinite;
|
-webkit-animation: p-progressbar-indeterminate-anim 2.1s
|
||||||
animation: p-progressbar-indeterminate-anim 2.1s cubic-bezier(0.65, 0.815, 0.735, 0.395) infinite;
|
cubic-bezier(0.65, 0.815, 0.735, 0.395) infinite;
|
||||||
|
animation: p-progressbar-indeterminate-anim 2.1s
|
||||||
|
cubic-bezier(0.65, 0.815, 0.735, 0.395) infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-progressbar-indeterminate .p-progressbar-value::after {
|
.p-progressbar-indeterminate .p-progressbar-value::after {
|
||||||
content: '';
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
background-color: inherit;
|
background-color: inherit;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
will-change: left, right;
|
will-change: left, right;
|
||||||
-webkit-animation: p-progressbar-indeterminate-anim-short 2.1s cubic-bezier(0.165, 0.84, 0.44, 1) infinite;
|
-webkit-animation: p-progressbar-indeterminate-anim-short 2.1s
|
||||||
animation: p-progressbar-indeterminate-anim-short 2.1s cubic-bezier(0.165, 0.84, 0.44, 1) infinite;
|
cubic-bezier(0.165, 0.84, 0.44, 1) infinite;
|
||||||
|
animation: p-progressbar-indeterminate-anim-short 2.1s
|
||||||
|
cubic-bezier(0.165, 0.84, 0.44, 1) infinite;
|
||||||
-webkit-animation-delay: 1.15s;
|
-webkit-animation-delay: 1.15s;
|
||||||
animation-delay: 1.15s;
|
animation-delay: 1.15s;
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.p-progress-spinner::before {
|
.p-progress-spinner::before {
|
||||||
content: '';
|
content: "";
|
||||||
display: block;
|
display: block;
|
||||||
padding-top: 100%;
|
padding-top: 100%;
|
||||||
}
|
}
|
||||||
@ -34,7 +34,9 @@
|
|||||||
stroke-dasharray: 89, 200;
|
stroke-dasharray: 89, 200;
|
||||||
stroke-dashoffset: 0;
|
stroke-dashoffset: 0;
|
||||||
stroke: $progressSpinnerStrokeColor;
|
stroke: $progressSpinnerStrokeColor;
|
||||||
animation: p-progress-spinner-dash 1.5s ease-in-out infinite, p-progress-spinner-color 6s ease-in-out infinite;
|
animation:
|
||||||
|
p-progress-spinner-dash 1.5s ease-in-out infinite,
|
||||||
|
p-progress-spinner-color 6s ease-in-out infinite;
|
||||||
stroke-linecap: round;
|
stroke-linecap: round;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.p-skeleton::after {
|
.p-skeleton::after {
|
||||||
content: '';
|
content: "";
|
||||||
animation: p-skeleton-animation 1.2s infinite;
|
animation: p-skeleton-animation 1.2s infinite;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
left: 0;
|
left: 0;
|
||||||
@ -38,6 +38,11 @@
|
|||||||
border-radius: $borderRadius;
|
border-radius: $borderRadius;
|
||||||
|
|
||||||
&:after {
|
&:after {
|
||||||
background: linear-gradient(90deg, rgba(255, 255, 255, 0), $skeletonAnimationBg, rgba(255, 255, 255, 0));
|
background: linear-gradient(
|
||||||
|
90deg,
|
||||||
|
rgba(255, 255, 255, 0),
|
||||||
|
$skeletonAnimationBg,
|
||||||
|
rgba(255, 255, 255, 0)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -52,12 +52,16 @@
|
|||||||
transition: opacity 0.2s ease-in-out;
|
transition: opacity 0.2s ease-in-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-galleria-item-nav-onhover .p-galleria-item-wrapper:hover .p-galleria-item-nav {
|
.p-galleria-item-nav-onhover
|
||||||
|
.p-galleria-item-wrapper:hover
|
||||||
|
.p-galleria-item-nav {
|
||||||
pointer-events: all;
|
pointer-events: all;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-galleria-item-nav-onhover .p-galleria-item-wrapper:hover .p-galleria-item-nav.p-disabled {
|
.p-galleria-item-nav-onhover
|
||||||
|
.p-galleria-item-wrapper:hover
|
||||||
|
.p-galleria-item-nav.p-disabled {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -206,14 +210,16 @@
|
|||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-galleria-indicator-onitem.p-galleria-indicators-right .p-galleria-indicators {
|
.p-galleria-indicator-onitem.p-galleria-indicators-right
|
||||||
|
.p-galleria-indicators {
|
||||||
right: 0;
|
right: 0;
|
||||||
top: 0;
|
top: 0;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
align-items: flex-end;
|
align-items: flex-end;
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-galleria-indicator-onitem.p-galleria-indicators-bottom .p-galleria-indicators {
|
.p-galleria-indicator-onitem.p-galleria-indicators-bottom
|
||||||
|
.p-galleria-indicators {
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
@ -22,7 +22,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.p-confirm-popup-enter-active {
|
.p-confirm-popup-enter-active {
|
||||||
transition: transform 0.12s cubic-bezier(0, 0, 0.2, 1), opacity 0.12s cubic-bezier(0, 0, 0.2, 1);
|
transition:
|
||||||
|
transform 0.12s cubic-bezier(0, 0, 0.2, 1),
|
||||||
|
opacity 0.12s cubic-bezier(0, 0, 0.2, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-confirm-popup-leave-active {
|
.p-confirm-popup-leave-active {
|
||||||
@ -33,7 +35,7 @@
|
|||||||
.p-confirm-popup:before {
|
.p-confirm-popup:before {
|
||||||
bottom: 100%;
|
bottom: 100%;
|
||||||
left: calc(var(--overlayArrowLeft, 0) + 1.25rem);
|
left: calc(var(--overlayArrowLeft, 0) + 1.25rem);
|
||||||
content: ' ';
|
content: " ";
|
||||||
height: 0;
|
height: 0;
|
||||||
width: 0;
|
width: 0;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -104,13 +106,15 @@
|
|||||||
&:before {
|
&:before {
|
||||||
border-style: solid;
|
border-style: solid;
|
||||||
|
|
||||||
@if (nth($overlayContentBorder, 2) == 'none') {
|
@if (nth($overlayContentBorder, 2) == "none") {
|
||||||
border-color: rgba($overlayContentBg, 0);
|
border-color: rgba($overlayContentBg, 0);
|
||||||
border-bottom-color: scale-color($overlayContentBg, $lightness: -5%);
|
border-bottom-color: scale-color($overlayContentBg, $lightness: -5%);
|
||||||
}
|
} @else {
|
||||||
@else {
|
|
||||||
border-color: rgba(nth($overlayContentBorder, 3), 0);
|
border-color: rgba(nth($overlayContentBorder, 3), 0);
|
||||||
border-bottom-color: scale-color(nth($overlayContentBorder, 3), $lightness: -5%);
|
border-bottom-color: scale-color(
|
||||||
|
nth($overlayContentBorder, 3),
|
||||||
|
$lightness: -5%
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,10 +124,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&:before {
|
&:before {
|
||||||
@if (nth($overlayContentBorder, 2) == 'none') {
|
@if (nth($overlayContentBorder, 2) == "none") {
|
||||||
border-top-color: $overlayContentBg;
|
border-top-color: $overlayContentBg;
|
||||||
}
|
} @else {
|
||||||
@else {
|
|
||||||
border-top-color: nth($overlayContentBorder, 3);
|
border-top-color: nth($overlayContentBorder, 3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
@use 'sass:math';
|
@use "sass:math";
|
||||||
|
|
||||||
// core
|
// core
|
||||||
.p-overlaypanel {
|
.p-overlaypanel {
|
||||||
@ -29,7 +29,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.p-overlaypanel-enter-active {
|
.p-overlaypanel-enter-active {
|
||||||
transition: transform 0.12s cubic-bezier(0, 0, 0.2, 1), opacity 0.12s cubic-bezier(0, 0, 0.2, 1);
|
transition:
|
||||||
|
transform 0.12s cubic-bezier(0, 0, 0.2, 1),
|
||||||
|
opacity 0.12s cubic-bezier(0, 0, 0.2, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-overlaypanel-leave-active {
|
.p-overlaypanel-leave-active {
|
||||||
@ -40,7 +42,7 @@
|
|||||||
.p-overlaypanel:before {
|
.p-overlaypanel:before {
|
||||||
bottom: 100%;
|
bottom: 100%;
|
||||||
left: calc(var(--overlayArrowLeft, 0) + 1.25rem);
|
left: calc(var(--overlayArrowLeft, 0) + 1.25rem);
|
||||||
content: ' ';
|
content: " ";
|
||||||
height: 0;
|
height: 0;
|
||||||
width: 0;
|
width: 0;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -109,13 +111,15 @@
|
|||||||
&:before {
|
&:before {
|
||||||
border-style: solid;
|
border-style: solid;
|
||||||
|
|
||||||
@if (nth($overlayContentBorder, 2) == 'none') {
|
@if (nth($overlayContentBorder, 2) == "none") {
|
||||||
border-color: rgba($overlayContentBg, 0);
|
border-color: rgba($overlayContentBg, 0);
|
||||||
border-bottom-color: scale-color($overlayContentBg, $lightness: -5%);
|
border-bottom-color: scale-color($overlayContentBg, $lightness: -5%);
|
||||||
}
|
} @else {
|
||||||
@else {
|
|
||||||
border-color: rgba(nth($overlayContentBorder, 3), 0);
|
border-color: rgba(nth($overlayContentBorder, 3), 0);
|
||||||
border-bottom-color: scale-color(nth($overlayContentBorder, 3), $lightness: -5%);
|
border-bottom-color: scale-color(
|
||||||
|
nth($overlayContentBorder, 3),
|
||||||
|
$lightness: -5%
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,10 +129,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&:before {
|
&:before {
|
||||||
@if (nth($overlayContentBorder, 2) == 'none') {
|
@if (nth($overlayContentBorder, 2) == "none") {
|
||||||
border-top-color: $overlayContentBg;
|
border-top-color: $overlayContentBg;
|
||||||
}
|
} @else {
|
||||||
@else {
|
|
||||||
border-top-color: nth($overlayContentBorder, 3);
|
border-top-color: nth($overlayContentBorder, 3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,19 +1,19 @@
|
|||||||
// core
|
// core
|
||||||
.p-tooltip {
|
.p-tooltip {
|
||||||
position:absolute;
|
position: absolute;
|
||||||
display:none;
|
display: none;
|
||||||
padding: .25em .5rem;
|
padding: 0.25em 0.5rem;
|
||||||
max-width: 12.5rem;
|
max-width: 12.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-tooltip.p-tooltip-right,
|
.p-tooltip.p-tooltip-right,
|
||||||
.p-tooltip.p-tooltip-left {
|
.p-tooltip.p-tooltip-left {
|
||||||
padding: 0 .25rem;
|
padding: 0 0.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-tooltip.p-tooltip-top,
|
.p-tooltip.p-tooltip-top,
|
||||||
.p-tooltip.p-tooltip-bottom {
|
.p-tooltip.p-tooltip-bottom {
|
||||||
padding:.25em 0;
|
padding: 0.25em 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-tooltip .p-tooltip-text {
|
.p-tooltip .p-tooltip-text {
|
||||||
@ -31,27 +31,27 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.p-tooltip-right .p-tooltip-arrow {
|
.p-tooltip-right .p-tooltip-arrow {
|
||||||
margin-top: -.25rem;
|
margin-top: -0.25rem;
|
||||||
border-width: .25em .25em .25em 0;
|
border-width: 0.25em 0.25em 0.25em 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-tooltip-left .p-tooltip-arrow {
|
.p-tooltip-left .p-tooltip-arrow {
|
||||||
margin-top: -.25rem;
|
margin-top: -0.25rem;
|
||||||
border-width: .25em 0 .25em .25rem;
|
border-width: 0.25em 0 0.25em 0.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-tooltip.p-tooltip-top {
|
.p-tooltip.p-tooltip-top {
|
||||||
padding: .25em 0;
|
padding: 0.25em 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-tooltip-top .p-tooltip-arrow {
|
.p-tooltip-top .p-tooltip-arrow {
|
||||||
margin-left: -.25rem;
|
margin-left: -0.25rem;
|
||||||
border-width: .25em .25em 0;
|
border-width: 0.25em 0.25em 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-tooltip-bottom .p-tooltip-arrow {
|
.p-tooltip-bottom .p-tooltip-arrow {
|
||||||
margin-left: -.25rem;
|
margin-left: -0.25rem;
|
||||||
border-width: 0 .25em .25rem;
|
border-width: 0 0.25em 0.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
// theme
|
// theme
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
top: 50%;
|
top: 50%;
|
||||||
left: 0;
|
left: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
content: '';
|
content: "";
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-divider-content {
|
.p-divider-content {
|
||||||
@ -33,7 +33,7 @@
|
|||||||
top: 0;
|
top: 0;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
content: '';
|
content: "";
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-divider {
|
.p-divider {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
@use 'sass:math';
|
@use "sass:math";
|
||||||
|
|
||||||
//core
|
//core
|
||||||
.p-stepper .p-stepper-nav {
|
.p-stepper .p-stepper-nav {
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
@mixin focused-ring($ring-color) {
|
@mixin focused-ring($ring-color) {
|
||||||
box-shadow: 0 0 0 2px #1c2127, 0 0 0 4px $ring-color, 0 1px 2px 0 rgba(0, 0, 0, 0.0);
|
box-shadow:
|
||||||
|
0 0 0 2px #1c2127,
|
||||||
|
0 0 0 4px $ring-color,
|
||||||
|
0 1px 2px 0 rgba(0, 0, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@layer primevue {
|
@layer primevue {
|
||||||
@ -9,13 +12,19 @@
|
|||||||
|
|
||||||
.p-selectbutton > .p-button,
|
.p-selectbutton > .p-button,
|
||||||
.p-togglebutton.p-button {
|
.p-togglebutton.p-button {
|
||||||
transition: background-color $transitionDuration, border-color $transitionDuration, box-shadow $transitionDuration;
|
transition:
|
||||||
|
background-color $transitionDuration,
|
||||||
|
border-color $transitionDuration,
|
||||||
|
box-shadow $transitionDuration;
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-accordion {
|
.p-accordion {
|
||||||
.p-accordion-header {
|
.p-accordion-header {
|
||||||
.p-accordion-header-link {
|
.p-accordion-header-link {
|
||||||
transition: background-color $transitionDuration, border-color $transitionDuration, box-shadow $transitionDuration;
|
transition:
|
||||||
|
background-color $transitionDuration,
|
||||||
|
border-color $transitionDuration,
|
||||||
|
box-shadow $transitionDuration;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -24,7 +33,10 @@
|
|||||||
.p-tabview-nav {
|
.p-tabview-nav {
|
||||||
li {
|
li {
|
||||||
.p-tabview-nav-link {
|
.p-tabview-nav-link {
|
||||||
transition: background-color $transitionDuration, border-color $transitionDuration, box-shadow $transitionDuration;
|
transition:
|
||||||
|
background-color $transitionDuration,
|
||||||
|
border-color $transitionDuration,
|
||||||
|
box-shadow $transitionDuration;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -34,7 +46,10 @@
|
|||||||
.p-tabmenu-nav {
|
.p-tabmenu-nav {
|
||||||
.p-tabmenuitem {
|
.p-tabmenuitem {
|
||||||
.p-menuitem-link {
|
.p-menuitem-link {
|
||||||
transition: background-color $transitionDuration, border-color $transitionDuration, box-shadow $transitionDuration;
|
transition:
|
||||||
|
background-color $transitionDuration,
|
||||||
|
border-color $transitionDuration,
|
||||||
|
box-shadow $transitionDuration;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -54,31 +69,31 @@
|
|||||||
|
|
||||||
.p-button {
|
.p-button {
|
||||||
&:focus {
|
&:focus {
|
||||||
@include focused-ring(rgba($buttonBg, .7));
|
@include focused-ring(rgba($buttonBg, 0.7));
|
||||||
}
|
}
|
||||||
|
|
||||||
&.p-button-secondary:enabled:focus {
|
&.p-button-secondary:enabled:focus {
|
||||||
@include focused-ring(rgba($secondaryButtonBg, .7));
|
@include focused-ring(rgba($secondaryButtonBg, 0.7));
|
||||||
}
|
}
|
||||||
|
|
||||||
&.p-button-success:enabled:focus {
|
&.p-button-success:enabled:focus {
|
||||||
@include focused-ring(rgba($successButtonBg, .7));
|
@include focused-ring(rgba($successButtonBg, 0.7));
|
||||||
}
|
}
|
||||||
|
|
||||||
&.p-button-info:enabled:focus {
|
&.p-button-info:enabled:focus {
|
||||||
@include focused-ring(rgba($infoButtonBg, .7));
|
@include focused-ring(rgba($infoButtonBg, 0.7));
|
||||||
}
|
}
|
||||||
|
|
||||||
&.p-button-warning:enabled:focus {
|
&.p-button-warning:enabled:focus {
|
||||||
@include focused-ring(rgba($warningButtonBg, .7));
|
@include focused-ring(rgba($warningButtonBg, 0.7));
|
||||||
}
|
}
|
||||||
|
|
||||||
&.p-button-help:enabled:focus {
|
&.p-button-help:enabled:focus {
|
||||||
@include focused-ring(rgba($helpButtonBg, .7));
|
@include focused-ring(rgba($helpButtonBg, 0.7));
|
||||||
}
|
}
|
||||||
|
|
||||||
&.p-button-danger:enabled:focus {
|
&.p-button-danger:enabled:focus {
|
||||||
@include focused-ring(rgba($dangerButtonBg, .7));
|
@include focused-ring(rgba($dangerButtonBg, 0.7));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,7 +113,7 @@
|
|||||||
|
|
||||||
.p-speeddial-item {
|
.p-speeddial-item {
|
||||||
&.p-focus > .p-speeddial-action {
|
&.p-focus > .p-speeddial-action {
|
||||||
@include focused-ring(rgba($buttonBg, .7));
|
@include focused-ring(rgba($buttonBg, 0.7));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,7 +124,7 @@
|
|||||||
.p-message {
|
.p-message {
|
||||||
.p-message-close {
|
.p-message-close {
|
||||||
&:hover {
|
&:hover {
|
||||||
background: rgba(255,255,255,.1);
|
background: rgba(255, 255, 255, 0.1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -118,7 +133,7 @@
|
|||||||
.p-toast-message {
|
.p-toast-message {
|
||||||
.p-toast-icon-close {
|
.p-toast-icon-close {
|
||||||
&:hover {
|
&:hover {
|
||||||
background: rgba(255,255,255,.1);
|
background: rgba(255, 255, 255, 0.1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -130,7 +145,12 @@
|
|||||||
|
|
||||||
.p-picklist-buttons .p-button,
|
.p-picklist-buttons .p-button,
|
||||||
.p-orderlist-controls .p-button {
|
.p-orderlist-controls .p-button {
|
||||||
transition: opacity $transitionDuration, background-color $transitionDuration, color $transitionDuration, border-color $transitionDuration, box-shadow $transitionDuration;
|
transition:
|
||||||
|
opacity $transitionDuration,
|
||||||
|
background-color $transitionDuration,
|
||||||
|
color $transitionDuration,
|
||||||
|
border-color $transitionDuration,
|
||||||
|
box-shadow $transitionDuration;
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-steps {
|
.p-steps {
|
||||||
|
@ -41,7 +41,10 @@ $colors: (
|
|||||||
$shade000: rgba(255, 255, 255, 0.87) !default; //text color
|
$shade000: rgba(255, 255, 255, 0.87) !default; //text color
|
||||||
$shade100: rgba(255, 255, 255, 0.6) !default; //text secondary color
|
$shade100: rgba(255, 255, 255, 0.6) !default; //text secondary color
|
||||||
$shade500: #6b7280 !default;
|
$shade500: #6b7280 !default;
|
||||||
$shade600: map-get($colors, "htwk-grau-140") !default; //input bg, border, divider
|
$shade600: map-get(
|
||||||
|
$colors,
|
||||||
|
"htwk-grau-140"
|
||||||
|
) !default; //input bg, border, divider
|
||||||
$shade700: map-get($colors, "htwk-grau") !default; //menu bg
|
$shade700: map-get($colors, "htwk-grau") !default; //menu bg
|
||||||
$shade800: map-get($colors, "htwk-grau") !default; //elevated surface
|
$shade800: map-get($colors, "htwk-grau") !default; //elevated surface
|
||||||
$shade900: rgba(map-get($colors, "htwk-schwarz"), 1) !default; //ground surface
|
$shade900: rgba(map-get($colors, "htwk-schwarz"), 1) !default; //ground surface
|
||||||
@ -49,15 +52,33 @@ $shade900: rgba(map-get($colors, "htwk-schwarz"), 1) !default; //ground surface
|
|||||||
$hoverBg: rgba(255, 255, 255, 0.03) !default;
|
$hoverBg: rgba(255, 255, 255, 0.03) !default;
|
||||||
|
|
||||||
//global
|
//global
|
||||||
$fontFamily: "Source Sans Pro", -apple-system, BlinkMacSystemFont, Segoe UI, Twemoji Country Flags, Roboto, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol !default;
|
$fontFamily:
|
||||||
|
"Source Sans Pro",
|
||||||
|
-apple-system,
|
||||||
|
BlinkMacSystemFont,
|
||||||
|
Segoe UI,
|
||||||
|
Twemoji Country Flags,
|
||||||
|
Roboto,
|
||||||
|
Arial,
|
||||||
|
sans-serif,
|
||||||
|
Apple Color Emoji,
|
||||||
|
Segoe UI Emoji,
|
||||||
|
Segoe UI Symbol !default;
|
||||||
$fontSize: 1rem !default;
|
$fontSize: 1rem !default;
|
||||||
$fontWeight: normal !default;
|
$fontWeight: normal !default;
|
||||||
$textColor: $shade000 !default;
|
$textColor: $shade000 !default;
|
||||||
$textSecondaryColor: $shade100 !default;
|
$textSecondaryColor: $shade100 !default;
|
||||||
$borderRadius: 6px !default;
|
$borderRadius: 6px !default;
|
||||||
$transitionDuration: 0.2s !default;
|
$transitionDuration: 0.2s !default;
|
||||||
$formElementTransition: background-color $transitionDuration, color $transitionDuration, border-color $transitionDuration, box-shadow $transitionDuration !default;
|
$formElementTransition:
|
||||||
$actionIconTransition: background-color $transitionDuration, color $transitionDuration, box-shadow $transitionDuration !default;
|
background-color $transitionDuration,
|
||||||
|
color $transitionDuration,
|
||||||
|
border-color $transitionDuration,
|
||||||
|
box-shadow $transitionDuration !default;
|
||||||
|
$actionIconTransition:
|
||||||
|
background-color $transitionDuration,
|
||||||
|
color $transitionDuration,
|
||||||
|
box-shadow $transitionDuration !default;
|
||||||
$listItemTransition: box-shadow $transitionDuration !default;
|
$listItemTransition: box-shadow $transitionDuration !default;
|
||||||
$primeIconFontSize: 1rem !default;
|
$primeIconFontSize: 1rem !default;
|
||||||
$divider: 1px solid $shade600 !default;
|
$divider: 1px solid $shade600 !default;
|
||||||
@ -135,8 +156,10 @@ $inputListHeaderBorder: 1px solid $shade600 !default;
|
|||||||
$inputOverlayBg: $inputListBg !default;
|
$inputOverlayBg: $inputListBg !default;
|
||||||
$inputOverlayHeaderBg: $inputListHeaderBg !default;
|
$inputOverlayHeaderBg: $inputListHeaderBg !default;
|
||||||
$inputOverlayBorder: 1px solid $shade600 !default;
|
$inputOverlayBorder: 1px solid $shade600 !default;
|
||||||
$inputOverlayShadow: 0 2px 4px -1px rgba(0, 0, 0, 0.2),
|
$inputOverlayShadow:
|
||||||
0 4px 5px 0 rgba(0, 0, 0, 0.14), 0 1px 10px 0 rgba(0, 0, 0, 0.12) !default;
|
0 2px 4px -1px rgba(0, 0, 0, 0.2),
|
||||||
|
0 4px 5px 0 rgba(0, 0, 0, 0.14),
|
||||||
|
0 1px 10px 0 rgba(0, 0, 0, 0.12) !default;
|
||||||
|
|
||||||
//password
|
//password
|
||||||
$passwordMeterBg: $shade600 !default;
|
$passwordMeterBg: $shade600 !default;
|
||||||
@ -157,8 +180,10 @@ $buttonHoverBorderColor: $primaryLightColor !default;
|
|||||||
$buttonActiveBg: $primaryLighterColor !default;
|
$buttonActiveBg: $primaryLighterColor !default;
|
||||||
$buttonTextActiveColor: $primaryTextColor !default;
|
$buttonTextActiveColor: $primaryTextColor !default;
|
||||||
$buttonActiveBorderColor: $primaryLighterColor !default;
|
$buttonActiveBorderColor: $primaryLighterColor !default;
|
||||||
$raisedButtonShadow: 0px 3px 1px -2px rgba(0, 0, 0, 0.2),
|
$raisedButtonShadow:
|
||||||
0px 2px 2px 0px rgba(0, 0, 0, 0.14), 0px 1px 5px 0px rgba(0, 0, 0, 0.12) !default;
|
0px 3px 1px -2px rgba(0, 0, 0, 0.2),
|
||||||
|
0px 2px 2px 0px rgba(0, 0, 0, 0.14),
|
||||||
|
0px 1px 5px 0px rgba(0, 0, 0, 0.12) !default;
|
||||||
$roundedButtonBorderRadius: 2rem !default;
|
$roundedButtonBorderRadius: 2rem !default;
|
||||||
|
|
||||||
$textButtonHoverBgOpacity: 0.04 !default;
|
$textButtonHoverBgOpacity: 0.04 !default;
|
||||||
@ -189,7 +214,8 @@ $infoButtonHoverBorderColor: map-get($colors, "htwk-cyan") !default;
|
|||||||
$infoButtonActiveBg: map-get($colors, "htwk-cyan") !default;
|
$infoButtonActiveBg: map-get($colors, "htwk-cyan") !default;
|
||||||
$infoButtonTextActiveColor: $infoButtonTextColor !default;
|
$infoButtonTextActiveColor: $infoButtonTextColor !default;
|
||||||
$infoButtonActiveBorderColor: map-get($colors, "htwk-cyan") !default;
|
$infoButtonActiveBorderColor: map-get($colors, "htwk-cyan") !default;
|
||||||
$infoButtonFocusShadow: 0 0 0 1px scale-color($infoButtonHoverBg, $lightness: 30%) !default;
|
$infoButtonFocusShadow: 0 0 0 1px
|
||||||
|
scale-color($infoButtonHoverBg, $lightness: 30%) !default;
|
||||||
|
|
||||||
$successButtonBg: map-get($colors, "htwk-gruen") !default;
|
$successButtonBg: map-get($colors, "htwk-gruen") !default;
|
||||||
$successButtonTextColor: #052e16 !default;
|
$successButtonTextColor: #052e16 !default;
|
||||||
@ -208,10 +234,16 @@ $warningButtonTextColor: #493c08 !default;
|
|||||||
$warningButtonBorder: 1px solid map-get($colors, "htwk-yellow") !default;
|
$warningButtonBorder: 1px solid map-get($colors, "htwk-yellow") !default;
|
||||||
$warningButtonHoverBg: scale-color($warningButtonBg, $lightness: 30%) !default;
|
$warningButtonHoverBg: scale-color($warningButtonBg, $lightness: 30%) !default;
|
||||||
$warningButtonTextHoverColor: $warningButtonTextColor !default;
|
$warningButtonTextHoverColor: $warningButtonTextColor !default;
|
||||||
$warningButtonHoverBorderColor: scale-color($warningButtonBg, $lightness: 10%) !default;
|
$warningButtonHoverBorderColor: scale-color(
|
||||||
|
$warningButtonBg,
|
||||||
|
$lightness: 10%
|
||||||
|
) !default;
|
||||||
$warningButtonActiveBg: scale-color($warningButtonBg, $lightness: 30%) !default;
|
$warningButtonActiveBg: scale-color($warningButtonBg, $lightness: 30%) !default;
|
||||||
$warningButtonTextActiveColor: $warningButtonTextColor !default;
|
$warningButtonTextActiveColor: $warningButtonTextColor !default;
|
||||||
$warningButtonActiveBorderColor: scale-color($warningButtonBg, $lightness: 30%) !default;
|
$warningButtonActiveBorderColor: scale-color(
|
||||||
|
$warningButtonBg,
|
||||||
|
$lightness: 30%
|
||||||
|
) !default;
|
||||||
$warningButtonFocusShadow: 0 0 0 1px
|
$warningButtonFocusShadow: 0 0 0 1px
|
||||||
scale-color($warningButtonBg, $lightness: 30%) !default;
|
scale-color($warningButtonBg, $lightness: 30%) !default;
|
||||||
|
|
||||||
@ -478,7 +510,9 @@ $cardSubTitleFontWeight: 100 !default;
|
|||||||
$cardSubTitleColor: $shade100 !default;
|
$cardSubTitleColor: $shade100 !default;
|
||||||
$cardContentPadding: 1.25rem 0 !default;
|
$cardContentPadding: 1.25rem 0 !default;
|
||||||
$cardFooterPadding: 1.25rem 0 0 0 !default;
|
$cardFooterPadding: 1.25rem 0 0 0 !default;
|
||||||
$cardShadow: 0 2px 1px -1px rgba(0, 0, 0, 0.2), 0 1px 1px 0 rgba(0, 0, 0, 0.14),
|
$cardShadow:
|
||||||
|
0 2px 1px -1px rgba(0, 0, 0, 0.2),
|
||||||
|
0 1px 1px 0 rgba(0, 0, 0, 0.14),
|
||||||
0 1px 3px 0 rgba(0, 0, 0, 0.12) !default;
|
0 1px 3px 0 rgba(0, 0, 0, 0.12) !default;
|
||||||
|
|
||||||
//editor
|
//editor
|
||||||
@ -644,8 +678,10 @@ $contrastMessageIconColor: $contrastButtonTextColor !default;
|
|||||||
//overlays
|
//overlays
|
||||||
$overlayContentBorder: 1px solid $shade600 !default;
|
$overlayContentBorder: 1px solid $shade600 !default;
|
||||||
$overlayContentBg: $panelContentBg !default;
|
$overlayContentBg: $panelContentBg !default;
|
||||||
$overlayContainerShadow: 0px 11px 15px -7px rgba(0, 0, 0, 0.2),
|
$overlayContainerShadow:
|
||||||
0px 24px 38px 3px rgba(0, 0, 0, 0.14), 0px 9px 46px 8px rgba(0, 0, 0, 0.12) !default;
|
0px 11px 15px -7px rgba(0, 0, 0, 0.2),
|
||||||
|
0px 24px 38px 3px rgba(0, 0, 0, 0.14),
|
||||||
|
0px 9px 46px 8px rgba(0, 0, 0, 0.12) !default;
|
||||||
|
|
||||||
//dialog
|
//dialog
|
||||||
$dialogHeaderBg: $shade800 !default;
|
$dialogHeaderBg: $shade800 !default;
|
||||||
@ -719,8 +755,10 @@ $submenuHeaderBorderRadius: 0 !default;
|
|||||||
$submenuHeaderFontWeight: 0 !default;
|
$submenuHeaderFontWeight: 0 !default;
|
||||||
$overlayMenuBg: $menuBg !default;
|
$overlayMenuBg: $menuBg !default;
|
||||||
$overlayMenuBorder: 1px solid $shade600 !default;
|
$overlayMenuBorder: 1px solid $shade600 !default;
|
||||||
$overlayMenuShadow: 0 2px 4px -1px rgba(0, 0, 0, 0.2),
|
$overlayMenuShadow:
|
||||||
0 4px 5px 0 rgba(0, 0, 0, 0.14), 0 1px 10px 0 rgba(0, 0, 0, 0.12) !default;
|
0 2px 4px -1px rgba(0, 0, 0, 0.2),
|
||||||
|
0 4px 5px 0 rgba(0, 0, 0, 0.14),
|
||||||
|
0 1px 10px 0 rgba(0, 0, 0, 0.12) !default;
|
||||||
$verticalMenuPadding: 0.25rem 0 !default;
|
$verticalMenuPadding: 0.25rem 0 !default;
|
||||||
$menuSeparatorMargin: 0.25rem 0 !default;
|
$menuSeparatorMargin: 0.25rem 0 !default;
|
||||||
|
|
||||||
@ -887,10 +925,23 @@ $imagePreviewActionIconFontSize: 1.5rem !default;
|
|||||||
$imagePreviewActionIconBorderRadius: 50% !default;
|
$imagePreviewActionIconBorderRadius: 50% !default;
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
font-family: "Source Sans Pro", -apple-system, BlinkMacSystemFont, Segoe UI, Twemoji Country Flags, Roboto, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol;
|
font-family:
|
||||||
|
"Source Sans Pro",
|
||||||
|
-apple-system,
|
||||||
|
BlinkMacSystemFont,
|
||||||
|
Segoe UI,
|
||||||
|
Twemoji Country Flags,
|
||||||
|
Roboto,
|
||||||
|
Arial,
|
||||||
|
sans-serif,
|
||||||
|
Apple Color Emoji,
|
||||||
|
Segoe UI Emoji,
|
||||||
|
Segoe UI Symbol;
|
||||||
font-feature-settings: "cv02", "cv03", "cv04", "cv11";
|
font-feature-settings: "cv02", "cv03", "cv04", "cv11";
|
||||||
font-variation-settings: normal;
|
font-variation-settings: normal;
|
||||||
--font-family: "Source Sans Pro", -apple-system, BlinkMacSystemFont, Segoe UI, Twemoji Country Flags, Roboto, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol;
|
--font-family: "Source Sans Pro", -apple-system, BlinkMacSystemFont, Segoe UI,
|
||||||
|
Twemoji Country Flags, Roboto, Arial, sans-serif, Apple Color Emoji,
|
||||||
|
Segoe UI Emoji, Segoe UI Symbol;
|
||||||
--font-feature-settings: "cv02", "cv03", "cv04", "cv11";
|
--font-feature-settings: "cv02", "cv03", "cv04", "cv11";
|
||||||
--surface-a: #{$shade800};
|
--surface-a: #{$shade800};
|
||||||
--surface-b: #{$shade900};
|
--surface-b: #{$shade900};
|
||||||
|
@ -5,16 +5,21 @@ $primaryLightestColor: rgba(255, 237, 0, 0.1) !default;
|
|||||||
$primaryTextColor: #030712 !default;
|
$primaryTextColor: #030712 !default;
|
||||||
|
|
||||||
$highlightBg: rgba($primaryColor, 0.16) !default;
|
$highlightBg: rgba($primaryColor, 0.16) !default;
|
||||||
$highlightTextColor: rgba(255,255,255,0.87) !default;
|
$highlightTextColor: rgba(255, 255, 255, 0.87) !default;
|
||||||
$highlightFocusBg: rgba($primaryColor, 0.24) !default;
|
$highlightFocusBg: rgba($primaryColor, 0.24) !default;
|
||||||
|
|
||||||
@import '../_variables';
|
@import "../_variables";
|
||||||
@import './_fonts';
|
@import "./_fonts";
|
||||||
@import '../../../../theme-base/_components';
|
@import "../../../../theme-base/_components";
|
||||||
@import '../_extensions';
|
@import "../_extensions";
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
h1, h2, h3, h4, h5, h6 {
|
h1,
|
||||||
|
h2,
|
||||||
|
h3,
|
||||||
|
h4,
|
||||||
|
h5,
|
||||||
|
h6 {
|
||||||
font-weight: 100;
|
font-weight: 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,9 +155,15 @@ $highlightFocusBg: rgba($primaryColor, 0.24) !default;
|
|||||||
color: rgba(255, 255, 255, 0.87);
|
color: rgba(255, 255, 255, 0.87);
|
||||||
}
|
}
|
||||||
|
|
||||||
.fc.fc-unthemed .fc-toolbar .fc-button.fc-dayGridMonth-button.fc-button-active,
|
.fc.fc-unthemed
|
||||||
.fc.fc-unthemed .fc-toolbar .fc-button.fc-timeGridWeek-button.fc-button-active,
|
.fc-toolbar
|
||||||
.fc.fc-unthemed .fc-toolbar .fc-button.fc-timeGridDay-button.fc-button-active {
|
.fc-button.fc-dayGridMonth-button.fc-button-active,
|
||||||
|
.fc.fc-unthemed
|
||||||
|
.fc-toolbar
|
||||||
|
.fc-button.fc-timeGridWeek-button.fc-button-active,
|
||||||
|
.fc.fc-unthemed
|
||||||
|
.fc-toolbar
|
||||||
|
.fc-button.fc-timeGridDay-button.fc-button-active {
|
||||||
background: map-get($colors, "htwk-yellow"); /*#93c5fd*/
|
background: map-get($colors, "htwk-yellow"); /*#93c5fd*/
|
||||||
border-color: map-get($colors, "htwk-yellow"); /*#93c5fd*/
|
border-color: map-get($colors, "htwk-yellow"); /*#93c5fd*/
|
||||||
color: map-get($colors, "htwk-schwarz"); /*#1c2127*/
|
color: map-get($colors, "htwk-schwarz"); /*#1c2127*/
|
||||||
@ -460,6 +471,18 @@ $highlightFocusBg: rgba($primaryColor, 0.24) !default;
|
|||||||
|
|
||||||
.fc.fc-theme-standard .fc-highlight {
|
.fc.fc-theme-standard .fc-highlight {
|
||||||
color: rgba(255, 255, 255, 0.87);
|
color: rgba(255, 255, 255, 0.87);
|
||||||
background: rgba(map-get($colors, "htwk-yellow"), 0.16); /*rgba(147, 197, 253, 0.16)*/
|
background: rgba(
|
||||||
|
map-get($colors, "htwk-yellow"),
|
||||||
|
0.16
|
||||||
|
); /*rgba(147, 197, 253, 0.16)*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.fc-event-selected::after, .fc-event:focus::after {
|
||||||
|
background: var(--fc-event-selected-overlay-color);
|
||||||
|
inset: -1px;
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
z-index: 1;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
@ -1,5 +1,8 @@
|
|||||||
@mixin focused-ring($ring-color) {
|
@mixin focused-ring($ring-color) {
|
||||||
box-shadow: 0 0 0 2px #ffffff, 0 0 0 4px $ring-color, 0 1px 2px 0 rgba(0, 0, 0, 1.0);
|
box-shadow:
|
||||||
|
0 0 0 2px #ffffff,
|
||||||
|
0 0 0 4px $ring-color,
|
||||||
|
0 1px 2px 0 rgba(0, 0, 0, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@layer primevue {
|
@layer primevue {
|
||||||
@ -9,13 +12,19 @@
|
|||||||
|
|
||||||
.p-selectbutton > .p-button,
|
.p-selectbutton > .p-button,
|
||||||
.p-togglebutton.p-button {
|
.p-togglebutton.p-button {
|
||||||
transition: background-color $transitionDuration, border-color $transitionDuration, box-shadow $transitionDuration;
|
transition:
|
||||||
|
background-color $transitionDuration,
|
||||||
|
border-color $transitionDuration,
|
||||||
|
box-shadow $transitionDuration;
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-accordion {
|
.p-accordion {
|
||||||
.p-accordion-header {
|
.p-accordion-header {
|
||||||
.p-accordion-header-link {
|
.p-accordion-header-link {
|
||||||
transition: background-color $transitionDuration, border-color $transitionDuration, box-shadow $transitionDuration;
|
transition:
|
||||||
|
background-color $transitionDuration,
|
||||||
|
border-color $transitionDuration,
|
||||||
|
box-shadow $transitionDuration;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -24,7 +33,10 @@
|
|||||||
.p-tabview-nav {
|
.p-tabview-nav {
|
||||||
li {
|
li {
|
||||||
.p-tabview-nav-link {
|
.p-tabview-nav-link {
|
||||||
transition: background-color $transitionDuration, border-color $transitionDuration, box-shadow $transitionDuration;
|
transition:
|
||||||
|
background-color $transitionDuration,
|
||||||
|
border-color $transitionDuration,
|
||||||
|
box-shadow $transitionDuration;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -34,7 +46,10 @@
|
|||||||
.p-tabmenu-nav {
|
.p-tabmenu-nav {
|
||||||
.p-tabmenuitem {
|
.p-tabmenuitem {
|
||||||
.p-menuitem-link {
|
.p-menuitem-link {
|
||||||
transition: background-color $transitionDuration, border-color $transitionDuration, box-shadow $transitionDuration;
|
transition:
|
||||||
|
background-color $transitionDuration,
|
||||||
|
border-color $transitionDuration,
|
||||||
|
box-shadow $transitionDuration;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -112,7 +127,12 @@
|
|||||||
|
|
||||||
.p-picklist-buttons .p-button,
|
.p-picklist-buttons .p-button,
|
||||||
.p-orderlist-controls .p-button {
|
.p-orderlist-controls .p-button {
|
||||||
transition: opacity $transitionDuration, background-color $transitionDuration, color $transitionDuration, border-color $transitionDuration, box-shadow $transitionDuration;
|
transition:
|
||||||
|
opacity $transitionDuration,
|
||||||
|
background-color $transitionDuration,
|
||||||
|
color $transitionDuration,
|
||||||
|
border-color $transitionDuration,
|
||||||
|
box-shadow $transitionDuration;
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-steps {
|
.p-steps {
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -4,17 +4,22 @@ $primaryDarkColor: #002d67 !default;
|
|||||||
$primaryDarkerColor: #022541 !default;
|
$primaryDarkerColor: #022541 !default;
|
||||||
$primaryTextColor: #ffffff !default;
|
$primaryTextColor: #ffffff !default;
|
||||||
|
|
||||||
$highlightBg: #EFF6FF !default;
|
$highlightBg: #eff6ff !default;
|
||||||
$highlightTextColor: $primaryDarkerColor !default;
|
$highlightTextColor: $primaryDarkerColor !default;
|
||||||
$highlightFocusBg: rgba($primaryColor, .24) !default;
|
$highlightFocusBg: rgba($primaryColor, 0.24) !default;
|
||||||
|
|
||||||
@import '../_variables';
|
@import "../_variables";
|
||||||
@import './_fonts';
|
@import "./_fonts";
|
||||||
@import '../../../../theme-base/_components';
|
@import "../../../../theme-base/_components";
|
||||||
@import '../_extensions';
|
@import "../_extensions";
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
h1, h2, h3, h4, h5, h6 {
|
h1,
|
||||||
|
h2,
|
||||||
|
h3,
|
||||||
|
h4,
|
||||||
|
h5,
|
||||||
|
h6 {
|
||||||
font-weight: 100;
|
font-weight: 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,9 +144,15 @@ $highlightFocusBg: rgba($primaryColor, .24) !default;
|
|||||||
color: rgba(255, 255, 255, 0.87);
|
color: rgba(255, 255, 255, 0.87);
|
||||||
}
|
}
|
||||||
|
|
||||||
.fc.fc-unthemed .fc-toolbar .fc-button.fc-dayGridMonth-button.fc-button-active,
|
.fc.fc-unthemed
|
||||||
.fc.fc-unthemed .fc-toolbar .fc-button.fc-timeGridWeek-button.fc-button-active,
|
.fc-toolbar
|
||||||
.fc.fc-unthemed .fc-toolbar .fc-button.fc-timeGridDay-button.fc-button-active {
|
.fc-button.fc-dayGridMonth-button.fc-button-active,
|
||||||
|
.fc.fc-unthemed
|
||||||
|
.fc-toolbar
|
||||||
|
.fc-button.fc-timeGridWeek-button.fc-button-active,
|
||||||
|
.fc.fc-unthemed
|
||||||
|
.fc-toolbar
|
||||||
|
.fc-button.fc-timeGridDay-button.fc-button-active {
|
||||||
background: map-get($colors, "primary"); /*#93c5fd*/
|
background: map-get($colors, "primary"); /*#93c5fd*/
|
||||||
border-color: map-get($colors, "primary"); /*#93c5fd*/
|
border-color: map-get($colors, "primary"); /*#93c5fd*/
|
||||||
color: $primaryTextColor; /*#1c2127*/
|
color: $primaryTextColor; /*#1c2127*/
|
||||||
@ -449,6 +460,9 @@ $highlightFocusBg: rgba($primaryColor, .24) !default;
|
|||||||
|
|
||||||
.fc.fc-theme-standard .fc-highlight {
|
.fc.fc-theme-standard .fc-highlight {
|
||||||
color: rgba(255, 255, 255, 0.87);
|
color: rgba(255, 255, 255, 0.87);
|
||||||
background: rgba(map-get($colors, "primary"), 0.16); /*rgba(147, 197, 253, 0.16)*/
|
background: rgba(
|
||||||
|
map-get($colors, "primary"),
|
||||||
|
0.16
|
||||||
|
); /*rgba(147, 197, 253, 0.16)*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,22 +1,35 @@
|
|||||||
@use 'sass:color';
|
@use "sass:color";
|
||||||
|
|
||||||
$primaryColor: #00944c !default;
|
$primaryColor: #00944c !default;
|
||||||
$primaryLightColor: color.scale($primaryColor, $lightness: 10%, $saturation: -10%) !default;
|
$primaryLightColor: color.scale(
|
||||||
|
$primaryColor,
|
||||||
|
$lightness: 10%,
|
||||||
|
$saturation: -10%
|
||||||
|
) !default;
|
||||||
$primaryDarkColor: color.scale($primaryColor, $lightness: -40%) !default;
|
$primaryDarkColor: color.scale($primaryColor, $lightness: -40%) !default;
|
||||||
$primaryDarkerColor: color.scale($primaryColor, $lightness: -80%) !default;
|
$primaryDarkerColor: color.scale($primaryColor, $lightness: -80%) !default;
|
||||||
$primaryTextColor: #ffffff !default;
|
$primaryTextColor: #ffffff !default;
|
||||||
|
|
||||||
$highlightBg: color.scale($primaryColor, $lightness: 90%, $saturation: -80%) !default;
|
$highlightBg: color.scale(
|
||||||
|
$primaryColor,
|
||||||
|
$lightness: 90%,
|
||||||
|
$saturation: -80%
|
||||||
|
) !default;
|
||||||
$highlightTextColor: $primaryDarkerColor !default;
|
$highlightTextColor: $primaryDarkerColor !default;
|
||||||
$highlightFocusBg: rgba($primaryColor, .24) !default;
|
$highlightFocusBg: rgba($primaryColor, 0.24) !default;
|
||||||
|
|
||||||
@import '../_variables';
|
@import "../_variables";
|
||||||
@import './_fonts';
|
@import "./_fonts";
|
||||||
@import '../../../../theme-base/_components';
|
@import "../../../../theme-base/_components";
|
||||||
@import '../_extensions';
|
@import "../_extensions";
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
h1, h2, h3, h4, h5, h6 {
|
h1,
|
||||||
|
h2,
|
||||||
|
h3,
|
||||||
|
h4,
|
||||||
|
h5,
|
||||||
|
h6 {
|
||||||
font-weight: 100;
|
font-weight: 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,6 +76,10 @@ $highlightFocusBg: rgba($primaryColor, .24) !default;
|
|||||||
margin: 1%;
|
margin: 1%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.fc-v-event .fc-event-main {
|
||||||
|
color: map-get($colors, "htwk-schwarz"); /*#1c2127*/
|
||||||
|
}
|
||||||
|
|
||||||
.fc.fc-unthemed .fc-view-container .fc-divider {
|
.fc.fc-unthemed .fc-view-container .fc-divider {
|
||||||
background: $primaryTextColor; /*#071426*/
|
background: $primaryTextColor; /*#071426*/
|
||||||
border: 1px solid map-get($colors, "htwk-grau-140"); /*#0b213f*/
|
border: 1px solid map-get($colors, "htwk-grau-140"); /*#0b213f*/
|
||||||
@ -148,9 +165,15 @@ $highlightFocusBg: rgba($primaryColor, .24) !default;
|
|||||||
color: rgba(255, 255, 255, 0.87);
|
color: rgba(255, 255, 255, 0.87);
|
||||||
}
|
}
|
||||||
|
|
||||||
.fc.fc-unthemed .fc-toolbar .fc-button.fc-dayGridMonth-button.fc-button-active,
|
.fc.fc-unthemed
|
||||||
.fc.fc-unthemed .fc-toolbar .fc-button.fc-timeGridWeek-button.fc-button-active,
|
.fc-toolbar
|
||||||
.fc.fc-unthemed .fc-toolbar .fc-button.fc-timeGridDay-button.fc-button-active {
|
.fc-button.fc-dayGridMonth-button.fc-button-active,
|
||||||
|
.fc.fc-unthemed
|
||||||
|
.fc-toolbar
|
||||||
|
.fc-button.fc-timeGridWeek-button.fc-button-active,
|
||||||
|
.fc.fc-unthemed
|
||||||
|
.fc-toolbar
|
||||||
|
.fc-button.fc-timeGridDay-button.fc-button-active {
|
||||||
background: map-get($colors, "primary"); /*#93c5fd*/
|
background: map-get($colors, "primary"); /*#93c5fd*/
|
||||||
border-color: map-get($colors, "primary"); /*#93c5fd*/
|
border-color: map-get($colors, "primary"); /*#93c5fd*/
|
||||||
color: $primaryTextColor; /*#1c2127*/
|
color: $primaryTextColor; /*#1c2127*/
|
||||||
@ -458,6 +481,18 @@ $highlightFocusBg: rgba($primaryColor, .24) !default;
|
|||||||
|
|
||||||
.fc.fc-theme-standard .fc-highlight {
|
.fc.fc-theme-standard .fc-highlight {
|
||||||
color: rgba(255, 255, 255, 0.87);
|
color: rgba(255, 255, 255, 0.87);
|
||||||
background: rgba(map-get($colors, "primary"), 0.16); /*rgba(147, 197, 253, 0.16)*/
|
background: rgba(
|
||||||
|
map-get($colors, "primary"),
|
||||||
|
0.16
|
||||||
|
); /*rgba(147, 197, 253, 0.16)*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.fc-event-selected::after, .fc-event:focus::after {
|
||||||
|
background: var(--fc-event-selected-overlay-color);
|
||||||
|
inset: -1px;
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
z-index: 1;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@ -21,8 +21,12 @@ import MenuBar from "./components/MenuBar.vue";
|
|||||||
import { RouteRecordName, RouterView } from "vue-router";
|
import { RouteRecordName, RouterView } from "vue-router";
|
||||||
import CalendarPreview from "./components/CalendarPreview.vue";
|
import CalendarPreview from "./components/CalendarPreview.vue";
|
||||||
import moduleStore from "./store/moduleStore.ts";
|
import moduleStore from "./store/moduleStore.ts";
|
||||||
import { provide, ref } from "vue";
|
import { onMounted, provide, ref } from "vue";
|
||||||
import { VueQueryDevtools } from "@tanstack/vue-query-devtools";
|
import { VueQueryDevtools } from "@tanstack/vue-query-devtools";
|
||||||
|
import settingsStore from "@/store/settingsStore.ts";
|
||||||
|
import { setTheme } from "@/helpers/theme.ts";
|
||||||
|
import { usePrimeVue } from "primevue/config";
|
||||||
|
const primeVue = usePrimeVue();
|
||||||
|
|
||||||
const disabledPages = [
|
const disabledPages = [
|
||||||
"room-finder",
|
"room-finder",
|
||||||
@ -33,7 +37,7 @@ const disabledPages = [
|
|||||||
"edit-calendar",
|
"edit-calendar",
|
||||||
"rooms",
|
"rooms",
|
||||||
"free-rooms",
|
"free-rooms",
|
||||||
"room-schedule",
|
"room-schedule"
|
||||||
];
|
];
|
||||||
|
|
||||||
const store = moduleStore();
|
const store = moduleStore();
|
||||||
@ -49,14 +53,26 @@ const updateMobile = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
updateMobile();
|
updateMobile();
|
||||||
|
|
||||||
window.addEventListener("resize", updateMobile);
|
window.addEventListener("resize", updateMobile);
|
||||||
|
|
||||||
|
const settings = settingsStore;
|
||||||
|
const emit = defineEmits(["dark-mode-toggled"]);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
// set theme matching browser preference
|
||||||
|
settings().setDarkMode(window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches)
|
||||||
|
setTheme(settings, primeVue, emit);
|
||||||
|
window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", (e) => {
|
||||||
|
settings().setDarkMode(e.matches)
|
||||||
|
setTheme(settings, primeVue, emit);
|
||||||
|
});
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<MenuBar />
|
<MenuBar />
|
||||||
<RouterView v-slot="{ Component, route }">
|
<RouterView v-slot="{ Component, route }">
|
||||||
<transition name="scale" mode="out-in">
|
<transition mode="out-in" name="scale">
|
||||||
<div :key="route.name ?? ''" class="origin-near-top">
|
<div :key="route.name ?? ''" class="origin-near-top">
|
||||||
<component :is="Component" />
|
<component :is="Component" />
|
||||||
</div>
|
</div>
|
||||||
|
@ -78,12 +78,13 @@ export async function fetchRoomOccupancy(
|
|||||||
}
|
}
|
||||||
|
|
||||||
var roomOccupancyList: RoomOccupancyList = new RoomOccupancyList(
|
var roomOccupancyList: RoomOccupancyList = new RoomOccupancyList(
|
||||||
new Date(), 0, 0, []
|
new Date(),
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
[],
|
||||||
);
|
);
|
||||||
|
|
||||||
await fetch(
|
await fetch("/api/schedule/rooms?from=" + from_date + "&to=" + to_date)
|
||||||
"/api/schedule/rooms?from=" + from_date + "&to=" + to_date,
|
|
||||||
)
|
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
return response.arrayBuffer();
|
return response.arrayBuffer();
|
||||||
})
|
})
|
||||||
|
@ -104,7 +104,7 @@ const actions = computed(() => [
|
|||||||
label: t("calendarLink.toHTWKalendar"),
|
label: t("calendarLink.toHTWKalendar"),
|
||||||
icon: "pi pi-home",
|
icon: "pi pi-home",
|
||||||
command: forwardToHTWKalendar,
|
command: forwardToHTWKalendar,
|
||||||
}
|
},
|
||||||
]);
|
]);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -1,8 +1,29 @@
|
|||||||
<script setup lang="ts">
|
<!--
|
||||||
|
Calendar implementation for the HTWK Leipzig timetable. Evaluation and display of the individual dates in iCal format.
|
||||||
|
Copyright (C) 2024 HTWKalender support@htwkalender.de
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
import FullCalendar from "@fullcalendar/vue3";
|
import FullCalendar from "@fullcalendar/vue3";
|
||||||
import { computed, ComputedRef, inject, Ref, ref, watch } from "vue";
|
import { computed, ComputedRef, inject, Ref, ref, watch } from "vue";
|
||||||
import { CalendarOptions, DatesSetArg } from "@fullcalendar/core";
|
import {
|
||||||
|
CalendarOptions,
|
||||||
|
DatesSetArg,
|
||||||
|
EventClickArg,
|
||||||
|
} from "@fullcalendar/core";
|
||||||
import allLocales from "@fullcalendar/core/locales-all";
|
import allLocales from "@fullcalendar/core/locales-all";
|
||||||
import dayGridPlugin from "@fullcalendar/daygrid";
|
import dayGridPlugin from "@fullcalendar/daygrid";
|
||||||
import interactionPlugin from "@fullcalendar/interaction";
|
import interactionPlugin from "@fullcalendar/interaction";
|
||||||
@ -11,7 +32,7 @@ import iCalenderPlugin from "@fullcalendar/icalendar";
|
|||||||
import router from "@/router";
|
import router from "@/router";
|
||||||
import { formatYearMonthDay } from "@/helpers/dates.ts";
|
import { formatYearMonthDay } from "@/helpers/dates.ts";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
import { useQuery } from "@tanstack/vue-query";
|
import { useQuery, useQueryClient } from "@tanstack/vue-query";
|
||||||
import tokenStore from "@/store/tokenStore.ts";
|
import tokenStore from "@/store/tokenStore.ts";
|
||||||
import { parseICalData } from "@/helpers/ical.ts";
|
import { parseICalData } from "@/helpers/ical.ts";
|
||||||
import { fetchICalendarEvents } from "@/api/loadICal.ts";
|
import { fetchICalendarEvents } from "@/api/loadICal.ts";
|
||||||
@ -25,21 +46,68 @@ const props = defineProps({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const selectedToken = computed(() => props.token);
|
const op = ref();
|
||||||
|
const clickedEvent = ref();
|
||||||
|
|
||||||
|
const toggle = (info: EventClickArg) => {
|
||||||
|
const start = !info.event.start ? "" : info.event.start;
|
||||||
|
const end = !info.event.end ? "" : info.event.end;
|
||||||
|
|
||||||
|
if (op.value.visible) {
|
||||||
|
clickedEvent.value = null;
|
||||||
|
op.value.hide();
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
clickedEvent.value = {
|
||||||
|
title: info.event._def.title,
|
||||||
|
start: start,
|
||||||
|
end: end,
|
||||||
|
notes: info.event._def.extendedProps.notes,
|
||||||
|
allDay: info.event._def.allDay,
|
||||||
|
location: info.event._def.extendedProps.location,
|
||||||
|
};
|
||||||
|
op.value.show(info.jsEvent);
|
||||||
|
op.value.target = info.el;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const selectedToken = computed(() => {
|
||||||
|
return props.token;
|
||||||
|
});
|
||||||
|
|
||||||
const mobilePage = inject("mobilePage") as Ref<boolean>;
|
const mobilePage = inject("mobilePage") as Ref<boolean>;
|
||||||
const date: Ref<Date> = ref(new Date());
|
const date: Ref<Date> = ref(new Date());
|
||||||
|
|
||||||
const { data: calendar } = useQuery({
|
const { data: calendar} = useQuery({
|
||||||
queryKey: ["userCalendar", selectedToken],
|
queryKey: ["userCalendar", selectedToken],
|
||||||
queryFn: () =>
|
queryFn: () => fetchICalendarEvents(selectedToken.value),
|
||||||
fetchICalendarEvents(selectedToken.value),
|
|
||||||
select: (data) => {
|
select: (data) => {
|
||||||
return data;
|
return data;
|
||||||
},
|
},
|
||||||
|
staleTime: 12 * 60 * 60 * 1000, // 12 hours
|
||||||
|
refetchOnWindowFocus: "always",
|
||||||
|
refetchOnReconnect: "always",
|
||||||
networkMode: "offlineFirst",
|
networkMode: "offlineFirst",
|
||||||
enabled: () => tokenStore().token !== "",
|
enabled: () => tokenStore().token !== ""
|
||||||
staleTime: 5000000, // 500 seconds
|
});
|
||||||
|
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
const invalidateAndRefetchCalendar = () => {
|
||||||
|
console.debug("invalidateAndRefetchCalendar", selectedToken);
|
||||||
|
const queryKey = ["userCalendar", selectedToken];
|
||||||
|
queryClient.invalidateQueries({queryKey: queryKey}).then(() => {
|
||||||
|
queryClient.refetchQueries({queryKey: queryKey});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
invalidateAndRefetchCalendar
|
||||||
|
});
|
||||||
|
|
||||||
|
const events = computed(() => {
|
||||||
|
return parseICalData(calendar.value);
|
||||||
});
|
});
|
||||||
|
|
||||||
const fullCalendar = ref<InstanceType<typeof FullCalendar>>();
|
const fullCalendar = ref<InstanceType<typeof FullCalendar>>();
|
||||||
@ -58,6 +126,9 @@ const calendarOptions: ComputedRef<CalendarOptions> = computed(() => ({
|
|||||||
minute: "2-digit",
|
minute: "2-digit",
|
||||||
hour12: false,
|
hour12: false,
|
||||||
},
|
},
|
||||||
|
eventClick(info) {
|
||||||
|
toggle(info);
|
||||||
|
},
|
||||||
height: "auto",
|
height: "auto",
|
||||||
views: {
|
views: {
|
||||||
week: {
|
week: {
|
||||||
@ -112,7 +183,7 @@ const calendarOptions: ComputedRef<CalendarOptions> = computed(() => ({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
events: parseICalData(calendar.value),
|
events: events.value,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
watch(mobilePage, () => {
|
watch(mobilePage, () => {
|
||||||
@ -121,7 +192,22 @@ watch(mobilePage, () => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<FullCalendar ref="fullCalendar" :options="calendarOptions" />
|
<FullCalendar
|
||||||
|
id="overlay-mount-point"
|
||||||
|
ref="fullCalendar"
|
||||||
|
:options="calendarOptions"
|
||||||
|
>
|
||||||
|
</FullCalendar>
|
||||||
|
|
||||||
|
<OverlayPanel ref="op">
|
||||||
|
<div>
|
||||||
|
<h3>{{ clickedEvent.title }}</h3>
|
||||||
|
<p>Location: {{ clickedEvent.location }}</p>
|
||||||
|
<p>Start: {{ clickedEvent.start?.toLocaleString() }}</p>
|
||||||
|
<p>End: {{ clickedEvent.end?.toLocaleString() }}</p>
|
||||||
|
<p>Notes: {{ clickedEvent.notes }}</p>
|
||||||
|
</div>
|
||||||
|
</OverlayPanel>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
@ -17,53 +17,29 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, onMounted } from "vue";
|
import { toggleTheme } from "@/helpers/theme.ts";
|
||||||
|
import settingsStore from "@/store/settingsStore.ts";
|
||||||
|
import { computed } from "vue";
|
||||||
import { usePrimeVue } from "primevue/config";
|
import { usePrimeVue } from "primevue/config";
|
||||||
|
|
||||||
const PrimeVue = usePrimeVue();
|
const primeVue = usePrimeVue();
|
||||||
|
|
||||||
const emit = defineEmits(["dark-mode-toggled"]);
|
const emit = defineEmits(["dark-mode-toggled"]);
|
||||||
|
|
||||||
const isDark = ref(true);
|
const store = settingsStore;
|
||||||
const darkTheme = ref("lara-dark-blue"),
|
|
||||||
lightTheme = ref("lara-light-blue");
|
|
||||||
|
|
||||||
function toggleTheme() {
|
const isDark = computed(() => store().isDark);
|
||||||
isDark.value = !isDark.value;
|
|
||||||
setTheme(isDark.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
function setTheme(shouldBeDark: boolean) {
|
|
||||||
isDark.value = shouldBeDark;
|
|
||||||
const newTheme = isDark.value ? darkTheme.value : lightTheme.value,
|
|
||||||
oldTheme = isDark.value ? lightTheme.value : darkTheme.value;
|
|
||||||
PrimeVue.changeTheme(oldTheme, newTheme, "theme-link", () => {});
|
|
||||||
emit("dark-mode-toggled", isDark.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
// set theme matching browser preference
|
|
||||||
setTheme(
|
|
||||||
window.matchMedia &&
|
|
||||||
window.matchMedia("(prefers-color-scheme: dark)").matches,
|
|
||||||
);
|
|
||||||
|
|
||||||
window
|
|
||||||
.matchMedia("(prefers-color-scheme: dark)")
|
|
||||||
.addEventListener("change", (e) => {
|
|
||||||
setTheme(e.matches);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Button
|
<Button
|
||||||
id="dark-mode-switcher"
|
id="dark-mode-switcher"
|
||||||
size="small"
|
size="small"
|
||||||
class="p-button-rounded w-full md:w-auto"
|
class="p-button-rounded md:w-auto"
|
||||||
style="margin-right: 1rem"
|
style="margin-right: 1rem"
|
||||||
:severity="isDark ? 'warning' : 'success'"
|
:severity="isDark ? 'warning' : 'success'"
|
||||||
@click="toggleTheme();"
|
@click="toggleTheme(store, primeVue, emit)"
|
||||||
>
|
>
|
||||||
<i v-if="isDark" class="pi pi-sun"></i>
|
<i v-if="isDark" class="pi pi-sun"></i>
|
||||||
<i v-else class="pi pi-moon"></i>
|
<i v-else class="pi pi-moon"></i>
|
||||||
|
45
frontend/src/components/DefaultPageSwitcher.vue
Normal file
45
frontend/src/components/DefaultPageSwitcher.vue
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
<!--
|
||||||
|
Calendar implementation for the HTWK Leipzig timetable. Evaluation and display of the individual dates in iCal format.
|
||||||
|
Copyright (C) 2024 HTWKalender support@htwkalender.de
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed, ComputedRef, ref } from "vue";
|
||||||
|
import settingsStore from "../store/settingsStore.ts";
|
||||||
|
|
||||||
|
const pageOptions: ComputedRef<(string | {
|
||||||
|
label: string;
|
||||||
|
value: string;
|
||||||
|
})[]> = computed(() => [...settingsStore().getDefaultPageOptions()]);
|
||||||
|
|
||||||
|
const selectedPage = ref(settingsStore().defaultPage);
|
||||||
|
|
||||||
|
function updateDefaultPage(page: { label: string; value: string }) {
|
||||||
|
settingsStore().setDefaultPage(page);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateDefaultPage(settingsStore().defaultPage);
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<Dropdown
|
||||||
|
v-model="selectedPage"
|
||||||
|
:options="pageOptions"
|
||||||
|
placeholder="Select a Page"
|
||||||
|
class="w-full md:w-14rem"
|
||||||
|
option-label="label"
|
||||||
|
@change="updateDefaultPage($event.value)"
|
||||||
|
></Dropdown>
|
||||||
|
</template>
|
@ -18,7 +18,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed } from "vue";
|
import { computed } from "vue";
|
||||||
import localeStore from "../store/localeStore.ts";
|
import settingsStore from "../store/settingsStore.ts";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
import { usePrimeVue } from "primevue/config";
|
import { usePrimeVue } from "primevue/config";
|
||||||
import primeVue_de from "@/i18n/translations/primevue/prime_vue_local_de.json";
|
import primeVue_de from "@/i18n/translations/primevue/prime_vue_local_de.json";
|
||||||
@ -43,7 +43,7 @@ function displayCountry(code: string) {
|
|||||||
const primeVueConfig = usePrimeVue();
|
const primeVueConfig = usePrimeVue();
|
||||||
|
|
||||||
function updateLocale(locale: string) {
|
function updateLocale(locale: string) {
|
||||||
localeStore().setLocale(locale);
|
settingsStore().setLocale(locale);
|
||||||
|
|
||||||
if (locale === "de") {
|
if (locale === "de") {
|
||||||
primeVueConfig.config.locale = primeVue_de;
|
primeVueConfig.config.locale = primeVue_de;
|
||||||
@ -54,7 +54,7 @@ function updateLocale(locale: string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateLocale(localeStore().locale);
|
updateLocale(settingsStore().locale);
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<Dropdown
|
<Dropdown
|
||||||
@ -68,7 +68,14 @@ updateLocale(localeStore().locale);
|
|||||||
<template #value="slotProps">
|
<template #value="slotProps">
|
||||||
<div v-if="slotProps.value" class="flex align-items-center">
|
<div v-if="slotProps.value" class="flex align-items-center">
|
||||||
<div class="mr-2 flag">{{ displayIcon(slotProps.value) }}</div>
|
<div class="mr-2 flag">{{ displayIcon(slotProps.value) }}</div>
|
||||||
<div style="font-family: 'Twemoji Country Flags', 'Helvetica', 'Comic Sans', serif;">{{ displayCountry(slotProps.value) }}</div>
|
<div
|
||||||
|
style="
|
||||||
|
font-family: "Twemoji Country Flags",
|
||||||
|
"Helvetica", "Comic Sans", serif;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
{{ displayCountry(slotProps.value) }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<span v-else>
|
<span v-else>
|
||||||
{{ slotProps.placeholder }}
|
{{ slotProps.placeholder }}
|
||||||
|
@ -17,13 +17,11 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, ref } from "vue";
|
import { computed } from "vue";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
import LocaleSwitcher from "./LocaleSwitcher.vue";
|
|
||||||
import DarkModeSwitcher from "./DarkModeSwitcher.vue";
|
|
||||||
const { t } = useI18n({ useScope: "global" });
|
const { t } = useI18n({ useScope: "global" });
|
||||||
|
|
||||||
const isDark = ref(true);
|
|
||||||
|
|
||||||
const items = computed(() => [
|
const items = computed(() => [
|
||||||
{
|
{
|
||||||
@ -67,7 +65,7 @@ const items = computed(() => [
|
|||||||
label: t("roomFinderPage.roomSchedule") + " (offline)",
|
label: t("roomFinderPage.roomSchedule") + " (offline)",
|
||||||
icon: "pi pi-fw pi-ban",
|
icon: "pi pi-fw pi-ban",
|
||||||
route: "/rooms/occupancy/offline",
|
route: "/rooms/occupancy/offline",
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -86,13 +84,6 @@ const items = computed(() => [
|
|||||||
url: "https://www.htwk-leipzig.de/hochschule/kontakt/datenschutzerklaerung/",
|
url: "https://www.htwk-leipzig.de/hochschule/kontakt/datenschutzerklaerung/",
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
function handleDarkModeToggled(isDarkVar: boolean) {
|
|
||||||
// Do something with isDark value
|
|
||||||
// For example, update the root isDark value
|
|
||||||
// Assuming the root component has an isDark ref
|
|
||||||
isDark.value = isDarkVar;
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@ -140,8 +131,10 @@ function handleDarkModeToggled(isDarkVar: boolean) {
|
|||||||
</template>
|
</template>
|
||||||
<template #end>
|
<template #end>
|
||||||
<div class="flex align-items-stretch justify-content-center">
|
<div class="flex align-items-stretch justify-content-center">
|
||||||
<DarkModeSwitcher @dark-mode-toggled="handleDarkModeToggled"></DarkModeSwitcher>
|
<!-- Settings Button with Gear Icon -->
|
||||||
<LocaleSwitcher></LocaleSwitcher>
|
<router-link v-slot="{ navigate }" :to="`/settings`" custom>
|
||||||
|
<Button icon="pi pi-cog" severity="secondary" rounded text size="large" aria-label="Settings" @click="navigate" />
|
||||||
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</Menubar>
|
</Menubar>
|
||||||
|
@ -181,9 +181,7 @@ const calendarOptions: ComputedRef<CalendarOptions> = computed(() => ({
|
|||||||
borderColor: event.showFree
|
borderColor: event.showFree
|
||||||
? "var(--htwk-gruen-600)"
|
? "var(--htwk-gruen-600)"
|
||||||
: "var(--htwk-grau-60-600)",
|
: "var(--htwk-grau-60-600)",
|
||||||
textColor: event.showFree
|
textColor: event.showFree ? "var(--green-50)" : "white",
|
||||||
? "var(--green-50)"
|
|
||||||
: "white",
|
|
||||||
title: event.showFree
|
title: event.showFree
|
||||||
? t("roomFinderPage.available")
|
? t("roomFinderPage.available")
|
||||||
: t("roomFinderPage.occupied"),
|
: t("roomFinderPage.occupied"),
|
||||||
|
@ -79,7 +79,11 @@ const selectedRoom = computed(() => props.room);
|
|||||||
*/
|
*/
|
||||||
function transformData(data: RoomOccupancyList) {
|
function transformData(data: RoomOccupancyList) {
|
||||||
const events = data
|
const events = data
|
||||||
.decodeOccupancy(selectedRoom.value, new Date(currentDateFrom.value), new Date(currentDateTo.value))
|
.decodeOccupancy(
|
||||||
|
selectedRoom.value,
|
||||||
|
new Date(currentDateFrom.value),
|
||||||
|
new Date(currentDateTo.value),
|
||||||
|
)
|
||||||
.map((event, index) => ({
|
.map((event, index) => ({
|
||||||
id: index,
|
id: index,
|
||||||
event: event,
|
event: event,
|
||||||
|
123
frontend/src/helpers/ical.test.ts
Normal file
123
frontend/src/helpers/ical.test.ts
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
//Calendar implementation for the HTWK Leipzig timetable. Evaluation and display of the individual dates in iCal format.
|
||||||
|
//Copyright (C) 2024 HTWKalender support@htwkalender.de
|
||||||
|
|
||||||
|
//This program is free software: you can redistribute it and/or modify
|
||||||
|
//it under the terms of the GNU Affero General Public License as published by
|
||||||
|
//the Free Software Foundation, either version 3 of the License, or
|
||||||
|
//(at your option) any later version.
|
||||||
|
|
||||||
|
//This program is distributed in the hope that it will be useful,
|
||||||
|
//but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
//GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
//You should have received a copy of the GNU Affero General Public License
|
||||||
|
//along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import { expect, test } from "vitest";
|
||||||
|
import { exportedForTesting } from "@/helpers/ical.ts";
|
||||||
|
import { CalendarComponent } from "ical";
|
||||||
|
|
||||||
|
// colorizeEvents has only the function to colorize the events that are passed to it
|
||||||
|
test("colorizeEventsSameSummary", () => {
|
||||||
|
const events: CalendarComponent[] = [
|
||||||
|
{
|
||||||
|
type: "VEVENT",
|
||||||
|
summary: "Operations Research",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "VEVENT",
|
||||||
|
summary: "Operations Research",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
expect(exportedForTesting.colorizeEvents(events)).toEqual([
|
||||||
|
{ summary: "Operations Research", color: "var(--htwk-rot-200)" },
|
||||||
|
{ summary: "Operations Research", color: "var(--htwk-rot-200)" },
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("colorizeEventsDifferentSummary", () => {
|
||||||
|
const events: CalendarComponent[] = [
|
||||||
|
{
|
||||||
|
type: "VEVENT",
|
||||||
|
summary: "Algorithmische Mathematik",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "VEVENT",
|
||||||
|
summary: "Funktionale Programmierung",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
expect(exportedForTesting.colorizeEvents(events)).toEqual([
|
||||||
|
{ summary: "Algorithmische Mathematik", color: "var(--htwk-rot-200)" },
|
||||||
|
{ summary: "Funktionale Programmierung", color: "var(--htwk-gruen-300)" },
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("filterEventsDistinct", () => {
|
||||||
|
const events: CalendarComponent[] = [
|
||||||
|
{
|
||||||
|
type: "VEVENT",
|
||||||
|
summary: "Operations Research",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "VEVENT",
|
||||||
|
summary: "Operations Research",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
expect(exportedForTesting.filterEventsDistinct(events)).toEqual([
|
||||||
|
{ type: "VEVENT", summary: "Operations Research" },
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("filterEventsDistinctDifferentSummary", () => {
|
||||||
|
const events: CalendarComponent[] = [
|
||||||
|
{
|
||||||
|
type: "VEVENT",
|
||||||
|
summary: "Algorithmische Mathematik",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "VEVENT",
|
||||||
|
summary: "Funktionale Programmierung",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
expect(exportedForTesting.filterEventsDistinct(events)).toEqual(events);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("extractedColorizedEvents", () => {
|
||||||
|
const events: CalendarComponent[] = [
|
||||||
|
{
|
||||||
|
type: "VEVENT",
|
||||||
|
summary: "Operations Research",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "VEVENT",
|
||||||
|
summary: "Operations Research",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
expect(exportedForTesting.extractedColorizedEvents(events)).toEqual([
|
||||||
|
{ summary: "Operations Research", color: "var(--htwk-rot-200)" },
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("extractedColorizedEventsDifferentSummary", () => {
|
||||||
|
const events: CalendarComponent[] = [
|
||||||
|
{
|
||||||
|
type: "VEVENT",
|
||||||
|
summary: "Algorithmische Mathematik",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "VEVENT",
|
||||||
|
summary: "Funktionale Programmierung",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
expect(exportedForTesting.extractedColorizedEvents(events)).toEqual([
|
||||||
|
{ summary: "Algorithmische Mathematik", color: "var(--htwk-rot-200)" },
|
||||||
|
{ summary: "Funktionale Programmierung", color: "var(--htwk-gruen-300)" },
|
||||||
|
]);
|
||||||
|
});
|
@ -1,14 +1,50 @@
|
|||||||
import ICAL from 'ical.js';
|
//Calendar implementation for the HTWK Leipzig timetable. Evaluation and display of the individual dates in iCal format.
|
||||||
import { CalendarComponent } from 'ical';
|
//Copyright (C) 2024 HTWKalender support@htwkalender.de
|
||||||
|
|
||||||
export function parseICalData(icalData: string | undefined) {
|
//This program is free software: you can redistribute it and/or modify
|
||||||
|
//it under the terms of the GNU Affero General Public License as published by
|
||||||
|
//the Free Software Foundation, either version 3 of the License, or
|
||||||
|
//(at your option) any later version.
|
||||||
|
|
||||||
|
//This program is distributed in the hope that it will be useful,
|
||||||
|
//but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
//GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
//You should have received a copy of the GNU Affero General Public License
|
||||||
|
//along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import ICAL from "ical.js";
|
||||||
|
import { CalendarComponent } from "ical";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for the color distinction event
|
||||||
|
* @param title Event name
|
||||||
|
* @param color Color code for the event
|
||||||
|
*/
|
||||||
|
export interface ColorDistinctionEvent {
|
||||||
|
summary: string | undefined;
|
||||||
|
color: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses iCal data and returns an array of calendar components
|
||||||
|
* @param icalData iCal data to parse
|
||||||
|
* @returns Array of calendar components
|
||||||
|
*/
|
||||||
|
export function parseICalData(
|
||||||
|
icalData: string | undefined,
|
||||||
|
): CalendarComponent[] {
|
||||||
if (icalData === undefined || !icalData) {
|
if (icalData === undefined || !icalData) {
|
||||||
return [];
|
return [];
|
||||||
}
|
} else {
|
||||||
|
|
||||||
const jCalData = ICAL.parse(icalData);
|
const jCalData = ICAL.parse(icalData);
|
||||||
const comp = new ICAL.Component(jCalData);
|
const comp = new ICAL.Component(jCalData);
|
||||||
const vEvents = comp.getAllSubcomponents('vevent');
|
const vEvents = comp.getAllSubcomponents("vevent");
|
||||||
|
const events: CalendarComponent[] = vEvents.map((vevent: CalendarComponent) => {
|
||||||
|
return new ICAL.Event(vevent);
|
||||||
|
});
|
||||||
|
const colorDistinctionEvents: ColorDistinctionEvent[] = extractedColorizedEvents(events);
|
||||||
|
|
||||||
return vEvents.map((vevent: CalendarComponent) => {
|
return vEvents.map((vevent: CalendarComponent) => {
|
||||||
const event = new ICAL.Event(vevent);
|
const event = new ICAL.Event(vevent);
|
||||||
@ -17,8 +53,86 @@ export function parseICalData(icalData: string | undefined) {
|
|||||||
title: event.summary,
|
title: event.summary,
|
||||||
start: event.startDate.toJSDate(),
|
start: event.startDate.toJSDate(),
|
||||||
end: event.endDate.toJSDate(),
|
end: event.endDate.toJSDate(),
|
||||||
|
notes: event.description,
|
||||||
allDay: event.startDate.isDate,
|
allDay: event.startDate.isDate,
|
||||||
// Include other properties as needed
|
color: colorDistinctionEvents.find(
|
||||||
|
(e: ColorDistinctionEvent) => e.summary === event.summary,
|
||||||
|
)?.color,
|
||||||
|
id: event.uid,
|
||||||
|
location: event.location,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts the event names and assigns a color to each event
|
||||||
|
* @param vEvents Array of calendar components
|
||||||
|
* @returns Array of objects with event name and color
|
||||||
|
*/
|
||||||
|
function extractedColorizedEvents(
|
||||||
|
vEvents: CalendarComponent[],
|
||||||
|
): ColorDistinctionEvent[] {
|
||||||
|
return colorizeEvents(filterEventsDistinct(vEvents));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filters out duplicate events
|
||||||
|
* @param vEvents Array of calendar components
|
||||||
|
* @returns Array of calendar components without duplicates
|
||||||
|
*/
|
||||||
|
function filterEventsDistinct(
|
||||||
|
vEvents: CalendarComponent[],
|
||||||
|
): CalendarComponent[] {
|
||||||
|
return vEvents.filter(
|
||||||
|
(vevent: CalendarComponent, index: number, self: CalendarComponent[]) => {
|
||||||
|
return (
|
||||||
|
self.findIndex((v) => {
|
||||||
|
return v.summary === vevent.summary;
|
||||||
|
}) === index
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assigns a color to each event
|
||||||
|
* @param vEvents Array of calendar components
|
||||||
|
* @returns Array of objects with event name and color
|
||||||
|
*/
|
||||||
|
function colorizeEvents(vEvents: CalendarComponent[]): ColorDistinctionEvent[] {
|
||||||
|
return vEvents.map((vevent: CalendarComponent) => {
|
||||||
|
const colors: string[] = [
|
||||||
|
"var(--htwk-rot-200)",
|
||||||
|
"var(--htwk-gruen-300)",
|
||||||
|
"var(--htwk-magenta-400)",
|
||||||
|
"var(--htwk-cyan-400)",
|
||||||
|
"var(--htwk-silbergrau-600)",
|
||||||
|
"var(--htwk-yellow-300)",
|
||||||
|
"var(--htwk-blau-300)",
|
||||||
|
"var(--htwk-dunkelblau-200)",
|
||||||
|
"var(--htwk-rot-400)",
|
||||||
|
"var(--htwk-gruen-400)",
|
||||||
|
"var(--htwk-blau-200)",
|
||||||
|
];
|
||||||
|
|
||||||
|
const randomColor =
|
||||||
|
colors[
|
||||||
|
vEvents.findIndex((e: CalendarComponent) => {
|
||||||
|
return e.summary === vevent.summary;
|
||||||
|
}) % colors.length
|
||||||
|
];
|
||||||
|
|
||||||
|
return {
|
||||||
|
summary: vevent.summary,
|
||||||
|
color: randomColor,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Exported for testing
|
||||||
|
export const exportedForTesting = {
|
||||||
|
extractedColorizedEvents,
|
||||||
|
filterEventsDistinct,
|
||||||
|
colorizeEvents,
|
||||||
|
};
|
||||||
|
46
frontend/src/helpers/theme.ts
Normal file
46
frontend/src/helpers/theme.ts
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
//Calendar implementation for the HTWK Leipzig timetable. Evaluation and display of the individual dates in iCal format.
|
||||||
|
//Copyright (C) 2024 HTWKalender support@htwkalender.de
|
||||||
|
|
||||||
|
//This program is free software: you can redistribute it and/or modify
|
||||||
|
//it under the terms of the GNU Affero General Public License as published by
|
||||||
|
//the Free Software Foundation, either version 3 of the License, or
|
||||||
|
//(at your option) any later version.
|
||||||
|
|
||||||
|
//This program is distributed in the hope that it will be useful,
|
||||||
|
//but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
//GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
//You should have received a copy of the GNU Affero General Public License
|
||||||
|
//along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import { ref } from "vue";
|
||||||
|
import { PrimeVueChangeTheme } from "primevue/config";
|
||||||
|
import { EmitFn } from "primevue/ts-helpers";
|
||||||
|
import settingsStore from "@/store/settingsStore.ts";
|
||||||
|
|
||||||
|
const darkTheme = ref("lara-dark-blue"),
|
||||||
|
lightTheme = ref("lara-light-blue");
|
||||||
|
|
||||||
|
export type SettingsStore = typeof settingsStore;
|
||||||
|
|
||||||
|
export function toggleTheme(
|
||||||
|
store: SettingsStore,
|
||||||
|
primeVue: { changeTheme: PrimeVueChangeTheme },
|
||||||
|
emit: EmitFn<"dark-mode-toggled"[]>,
|
||||||
|
): void {
|
||||||
|
store().setDarkMode(!store().isDark);
|
||||||
|
setTheme(store, primeVue, emit);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setTheme(
|
||||||
|
store: SettingsStore,
|
||||||
|
{ changeTheme }: { changeTheme: PrimeVueChangeTheme },
|
||||||
|
emit: EmitFn<"dark-mode-toggled"[]>
|
||||||
|
) {
|
||||||
|
const isDark = ref(store().isDark);
|
||||||
|
const newTheme = isDark.value ? darkTheme.value : lightTheme.value,
|
||||||
|
oldTheme = isDark.value ? lightTheme.value : darkTheme.value;
|
||||||
|
changeTheme(oldTheme, newTheme, "theme-link", () => { });
|
||||||
|
emit("dark-mode-toggled", isDark.value);
|
||||||
|
}
|
@ -1,3 +1,19 @@
|
|||||||
|
//Calendar implementation for the HTWK Leipzig timetable. Evaluation and display of the individual dates in iCal format.
|
||||||
|
//Copyright (C) 2024 HTWKalender support@htwkalender.de
|
||||||
|
|
||||||
|
//This program is free software: you can redistribute it and/or modify
|
||||||
|
//it under the terms of the GNU Affero General Public License as published by
|
||||||
|
//the Free Software Foundation, either version 3 of the License, or
|
||||||
|
//(at your option) any later version.
|
||||||
|
|
||||||
|
//This program is distributed in the hope that it will be useful,
|
||||||
|
//but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
//GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
//You should have received a copy of the GNU Affero General Public License
|
||||||
|
//along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
const tokenRegex = /^[a-z0-9]{15}$/;
|
const tokenRegex = /^[a-z0-9]{15}$/;
|
||||||
const tokenUriRegex = /[?&]token=([a-z0-9]{15})(?:&|$)/;
|
const tokenUriRegex = /[?&]token=([a-z0-9]{15})(?:&|$)/;
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ import { createI18n } from "vue-i18n";
|
|||||||
import en from "./translations/en.json";
|
import en from "./translations/en.json";
|
||||||
import de from "./translations/de.json";
|
import de from "./translations/de.json";
|
||||||
import ja from "./translations/ja.json";
|
import ja from "./translations/ja.json";
|
||||||
import localeStore from "../store/localeStore.ts";
|
import settingsStore from "../store/settingsStore.ts";
|
||||||
|
|
||||||
// Private instance of VueI18n object
|
// Private instance of VueI18n object
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
@ -27,7 +27,7 @@ let _i18n: any;
|
|||||||
function setup() {
|
function setup() {
|
||||||
_i18n = createI18n({
|
_i18n = createI18n({
|
||||||
legacy: false,
|
legacy: false,
|
||||||
locale: localeStore().locale,
|
locale: settingsStore().locale,
|
||||||
fallbackLocale: "en",
|
fallbackLocale: "en",
|
||||||
messages: {
|
messages: {
|
||||||
en,
|
en,
|
||||||
|
@ -11,6 +11,10 @@
|
|||||||
"english": "Englisch",
|
"english": "Englisch",
|
||||||
"german": "Deutsch",
|
"german": "Deutsch",
|
||||||
"japanese": "Japanisch",
|
"japanese": "Japanisch",
|
||||||
|
"notFound": {
|
||||||
|
"headline": "404",
|
||||||
|
"subTitle": "Seite nicht gefunden"
|
||||||
|
},
|
||||||
"courseSelection": {
|
"courseSelection": {
|
||||||
"headline": "Willkommen beim HTWKalender",
|
"headline": "Willkommen beim HTWKalender",
|
||||||
"winterSemester": "Wintersemester",
|
"winterSemester": "Wintersemester",
|
||||||
@ -90,7 +94,8 @@
|
|||||||
"success": "Erfolg",
|
"success": "Erfolg",
|
||||||
"error": "Fehler",
|
"error": "Fehler",
|
||||||
"successDetail": "Kalender erfolgreich gelöscht",
|
"successDetail": "Kalender erfolgreich gelöscht",
|
||||||
"errorDetail": "Fehler beim Löschen des Kalenders"
|
"errorDetail": "Fehler beim Löschen des Kalenders",
|
||||||
|
"successDetailLoad": "Kalender erfolgreich geladen"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"additionalModules": {
|
"additionalModules": {
|
||||||
@ -249,5 +254,12 @@
|
|||||||
"subTitle": "Hier findest du die Kalenderansicht von deinem persönlichen Feed.",
|
"subTitle": "Hier findest du die Kalenderansicht von deinem persönlichen Feed.",
|
||||||
"searchPlaceholder": "Token",
|
"searchPlaceholder": "Token",
|
||||||
"searchButton": "Kalender laden"
|
"searchButton": "Kalender laden"
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"headline": "Einstellungen",
|
||||||
|
"subTitle": "Hier kannst du deine Einstellungen bearbeiten.",
|
||||||
|
"language": "Sprache einstellen",
|
||||||
|
"darkMode": "Design auswählen",
|
||||||
|
"defaultPage": "Standardseite"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,11 @@
|
|||||||
"privacy": "privacy",
|
"privacy": "privacy",
|
||||||
"english": "English",
|
"english": "English",
|
||||||
"german": "German",
|
"german": "German",
|
||||||
"japanese" : "Japanese",
|
"japanese": "Japanese",
|
||||||
|
"notFound": {
|
||||||
|
"headline": "404",
|
||||||
|
"subTitle": "page not found"
|
||||||
|
},
|
||||||
"courseSelection": {
|
"courseSelection": {
|
||||||
"headline": "welcome to HTWKalender",
|
"headline": "welcome to HTWKalender",
|
||||||
"winterSemester": "winter semester",
|
"winterSemester": "winter semester",
|
||||||
@ -90,7 +94,8 @@
|
|||||||
"success": "Success",
|
"success": "Success",
|
||||||
"error": "Error",
|
"error": "Error",
|
||||||
"successDetail": "calendar successfully deleted",
|
"successDetail": "calendar successfully deleted",
|
||||||
"errorDetail": "calendar could not be deleted"
|
"errorDetail": "calendar could not be deleted",
|
||||||
|
"successDetailLoad": "calendar successfully loaded"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"additionalModules": {
|
"additionalModules": {
|
||||||
@ -249,5 +254,12 @@
|
|||||||
"subTitle": "Here you can find the calendar view of your personal feed.",
|
"subTitle": "Here you can find the calendar view of your personal feed.",
|
||||||
"searchPlaceholder": "calendar token",
|
"searchPlaceholder": "calendar token",
|
||||||
"searchButton": "load calendar"
|
"searchButton": "load calendar"
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"headline": "Settings",
|
||||||
|
"subTitle": "Here you can change your settings.",
|
||||||
|
"language": "Choose your language",
|
||||||
|
"darkMode": "Switch page theme",
|
||||||
|
"defaultPage": "Default page"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,10 @@
|
|||||||
"english": "英語",
|
"english": "英語",
|
||||||
"german": "ドイツ語",
|
"german": "ドイツ語",
|
||||||
"japanese": "日本語",
|
"japanese": "日本語",
|
||||||
|
"notFound": {
|
||||||
|
"headline": "404",
|
||||||
|
"subTitle": "ページが見つかりません"
|
||||||
|
},
|
||||||
"courseSelection": {
|
"courseSelection": {
|
||||||
"headline": "HTWカレンダーへようこそ",
|
"headline": "HTWカレンダーへようこそ",
|
||||||
"winterSemester": "冬学期",
|
"winterSemester": "冬学期",
|
||||||
@ -90,7 +94,8 @@
|
|||||||
"success": "成功",
|
"success": "成功",
|
||||||
"error": "エラー",
|
"error": "エラー",
|
||||||
"successDetail": "カレンダーが正常に削除されました",
|
"successDetail": "カレンダーが正常に削除されました",
|
||||||
"errorDetail": "カレンダーを削除できませんでした"
|
"errorDetail": "カレンダーを削除できませんでした",
|
||||||
|
"successDetailLoad": "カレンダーが正常に読み込まれました"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"additionalModules": {
|
"additionalModules": {
|
||||||
@ -249,5 +254,12 @@
|
|||||||
"subTitle": "ここでは、個人のフィードのカレンダー表示を見つけることができます。",
|
"subTitle": "ここでは、個人のフィードのカレンダー表示を見つけることができます。",
|
||||||
"searchPlaceholder": "カレンダートークン",
|
"searchPlaceholder": "カレンダートークン",
|
||||||
"searchButton": "ロードカレンダー"
|
"searchButton": "ロードカレンダー"
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"headline": "設定",
|
||||||
|
"subTitle": "ここで設定を編集できます。",
|
||||||
|
"language": "言語",
|
||||||
|
"darkMode": "ダークモード",
|
||||||
|
"defaultPage": "デフォルトページ"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,41 +26,15 @@
|
|||||||
"金曜日",
|
"金曜日",
|
||||||
"土曜日"
|
"土曜日"
|
||||||
],
|
],
|
||||||
"dayNamesMin": [
|
"dayNamesMin": ["日", "月", "火", "水", "木", "金", "土"],
|
||||||
"日",
|
"dayNamesShort": ["日", "月", "火", "水", "木", "金", "土"],
|
||||||
"月",
|
|
||||||
"火",
|
|
||||||
"水",
|
|
||||||
"木",
|
|
||||||
"金",
|
|
||||||
"土"
|
|
||||||
],
|
|
||||||
"dayNamesShort": [
|
|
||||||
"日",
|
|
||||||
"月",
|
|
||||||
"火",
|
|
||||||
"水",
|
|
||||||
"木",
|
|
||||||
"金",
|
|
||||||
"土"
|
|
||||||
],
|
|
||||||
"emptyFilterMessage": "オプションなし",
|
"emptyFilterMessage": "オプションなし",
|
||||||
"emptyMessage": "結果なし",
|
"emptyMessage": "結果なし",
|
||||||
"emptySearchMessage": "該当なし",
|
"emptySearchMessage": "該当なし",
|
||||||
"emptySelectionMessage": "選択なし",
|
"emptySelectionMessage": "選択なし",
|
||||||
"endsWith": "終わる",
|
"endsWith": "終わる",
|
||||||
"equals": "等しい",
|
"equals": "等しい",
|
||||||
"fileSizeTypes": [
|
"fileSizeTypes": ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"],
|
||||||
"B",
|
|
||||||
"KB",
|
|
||||||
"MB",
|
|
||||||
"GB",
|
|
||||||
"TB",
|
|
||||||
"PB",
|
|
||||||
"EB",
|
|
||||||
"ZB",
|
|
||||||
"YB"
|
|
||||||
],
|
|
||||||
"filter": "フィルター",
|
"filter": "フィルター",
|
||||||
"firstDayOfWeek": 0,
|
"firstDayOfWeek": 0,
|
||||||
"gt": "超える",
|
"gt": "超える",
|
||||||
|
@ -31,6 +31,7 @@ import Card from "primevue/card";
|
|||||||
import DataView from "primevue/dataview";
|
import DataView from "primevue/dataview";
|
||||||
import Dialog from "primevue/dialog";
|
import Dialog from "primevue/dialog";
|
||||||
import Slider from "primevue/slider";
|
import Slider from "primevue/slider";
|
||||||
|
import OverlayPanel from "primevue/overlaypanel";
|
||||||
import ToggleButton from "primevue/togglebutton";
|
import ToggleButton from "primevue/togglebutton";
|
||||||
import "primeicons/primeicons.css";
|
import "primeicons/primeicons.css";
|
||||||
import "primeflex/primeflex.css";
|
import "primeflex/primeflex.css";
|
||||||
@ -56,23 +57,23 @@ import Calendar from "primevue/calendar";
|
|||||||
import i18n from "./i18n";
|
import i18n from "./i18n";
|
||||||
import { VueQueryPlugin } from "@tanstack/vue-query";
|
import { VueQueryPlugin } from "@tanstack/vue-query";
|
||||||
import { polyfillCountryFlagEmojis } from "country-flag-emoji-polyfill";
|
import { polyfillCountryFlagEmojis } from "country-flag-emoji-polyfill";
|
||||||
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
|
import piniaPluginPersistedstate from "pinia-plugin-persistedstate";
|
||||||
|
|
||||||
polyfillCountryFlagEmojis();
|
polyfillCountryFlagEmojis();
|
||||||
|
|
||||||
const app = createApp(App);
|
const app = createApp(App);
|
||||||
const pinia = createPinia();
|
const pinia = createPinia();
|
||||||
|
|
||||||
pinia.use(piniaPluginPersistedstate)
|
pinia.use(piniaPluginPersistedstate);
|
||||||
|
|
||||||
app.use(VueQueryPlugin, {
|
app.use(VueQueryPlugin, {
|
||||||
queryClientConfig: {
|
queryClientConfig: {
|
||||||
defaultOptions: {
|
defaultOptions: {
|
||||||
queries: {
|
queries: {
|
||||||
refetchOnWindowFocus: false,
|
refetchOnWindowFocus: false
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
app.use(PrimeVue);
|
app.use(PrimeVue);
|
||||||
@ -109,5 +110,6 @@ app.component("ProgressSpinner", ProgressSpinner);
|
|||||||
app.component("Checkbox", Checkbox);
|
app.component("Checkbox", Checkbox);
|
||||||
app.component("Skeleton", Skeleton);
|
app.component("Skeleton", Skeleton);
|
||||||
app.component("Calendar", Calendar);
|
app.component("Calendar", Calendar);
|
||||||
|
app.component("OverlayPanel", OverlayPanel);
|
||||||
|
|
||||||
app.mount("#app");
|
app.mount("#app");
|
||||||
|
@ -21,18 +21,18 @@ import { addHours, addMinutes, interval, subHours } from "date-fns";
|
|||||||
import { toZonedTime } from "date-fns-tz";
|
import { toZonedTime } from "date-fns-tz";
|
||||||
|
|
||||||
const testListStart = new Date("2022-01-01T00:00:00Z");
|
const testListStart = new Date("2022-01-01T00:00:00Z");
|
||||||
var testList : RoomOccupancyList; //= RoomOccupancyList.fromJSON({});
|
var testList: RoomOccupancyList; //= RoomOccupancyList.fromJSON({});
|
||||||
var alternating : Uint8Array = new Uint8Array(Array(4).fill(0xF0));
|
var alternating: Uint8Array = new Uint8Array(Array(4).fill(0xf0));
|
||||||
var booked : Uint8Array = new Uint8Array(Array(4).fill(0xFF));
|
var booked: Uint8Array = new Uint8Array(Array(4).fill(0xff));
|
||||||
var empty : Uint8Array = new Uint8Array(Array(4).fill(0x00));
|
var empty: Uint8Array = new Uint8Array(Array(4).fill(0x00));
|
||||||
var counting : Uint8Array = new Uint8Array([0x00, 0x01, 0x02, 0x03]);
|
var counting: Uint8Array = new Uint8Array([0x00, 0x01, 0x02, 0x03]);
|
||||||
|
|
||||||
const localTimezone = "Europe/Berlin";
|
const localTimezone = "Europe/Berlin";
|
||||||
|
|
||||||
describe("RoomOccupancyList", () => {
|
describe("RoomOccupancyList", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
alternating = new Uint8Array(Array(4).fill(0xF0));
|
alternating = new Uint8Array(Array(4).fill(0xf0));
|
||||||
booked = new Uint8Array(Array(4).fill(0xFF));
|
booked = new Uint8Array(Array(4).fill(0xff));
|
||||||
empty = new Uint8Array(Array(4).fill(0x00));
|
empty = new Uint8Array(Array(4).fill(0x00));
|
||||||
counting = new Uint8Array([0x00, 0x01, 0x02, 0x03]);
|
counting = new Uint8Array([0x00, 0x01, 0x02, 0x03]);
|
||||||
testList = RoomOccupancyList.fromJSON({
|
testList = RoomOccupancyList.fromJSON({
|
||||||
@ -55,8 +55,8 @@ describe("RoomOccupancyList", () => {
|
|||||||
{
|
{
|
||||||
name: "COUNTING",
|
name: "COUNTING",
|
||||||
occupancy: new Binary(counting, 0),
|
occupancy: new Binary(counting, 0),
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -66,12 +66,7 @@ describe("RoomOccupancyList", () => {
|
|||||||
const rooms = testList["getRooms"]();
|
const rooms = testList["getRooms"]();
|
||||||
|
|
||||||
// assert
|
// assert
|
||||||
expect(rooms).toEqual([
|
expect(rooms).toEqual(["BOOKED", "EMPTY", "ALTERNATING", "COUNTING"]);
|
||||||
"BOOKED",
|
|
||||||
"EMPTY",
|
|
||||||
"ALTERNATING",
|
|
||||||
"COUNTING"
|
|
||||||
]);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test("get empty rooms", () => {
|
test("get empty rooms", () => {
|
||||||
@ -80,7 +75,7 @@ describe("RoomOccupancyList", () => {
|
|||||||
start: testListStart,
|
start: testListStart,
|
||||||
granularity: 60,
|
granularity: 60,
|
||||||
blocks: 32,
|
blocks: 32,
|
||||||
rooms: []
|
rooms: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
// act
|
// act
|
||||||
@ -89,7 +84,7 @@ describe("RoomOccupancyList", () => {
|
|||||||
// assert
|
// assert
|
||||||
expect(rooms).toEqual([]);
|
expect(rooms).toEqual([]);
|
||||||
});
|
});
|
||||||
})
|
});
|
||||||
|
|
||||||
describe("decodeOccupancy", () => {
|
describe("decodeOccupancy", () => {
|
||||||
test("generate stubs for missing room", () => {
|
test("generate stubs for missing room", () => {
|
||||||
@ -109,8 +104,8 @@ describe("RoomOccupancyList", () => {
|
|||||||
end: "2022-01-01T08:00:00.000Z",
|
end: "2022-01-01T08:00:00.000Z",
|
||||||
rooms: "MISSING",
|
rooms: "MISSING",
|
||||||
free: true,
|
free: true,
|
||||||
stub: true
|
stub: true,
|
||||||
}
|
},
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -131,8 +126,8 @@ describe("RoomOccupancyList", () => {
|
|||||||
end: "2021-12-31T19:00:00.000Z",
|
end: "2021-12-31T19:00:00.000Z",
|
||||||
rooms: "BOOKED",
|
rooms: "BOOKED",
|
||||||
free: true,
|
free: true,
|
||||||
stub: true
|
stub: true,
|
||||||
}
|
},
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -151,48 +146,41 @@ describe("RoomOccupancyList", () => {
|
|||||||
end: "2022-01-01T08:00:00.000Z",
|
end: "2022-01-01T08:00:00.000Z",
|
||||||
rooms: "BOOKED",
|
rooms: "BOOKED",
|
||||||
free: false,
|
free: false,
|
||||||
stub: false
|
stub: false,
|
||||||
}
|
},
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("sliceOccupancy", () => {
|
describe("sliceOccupancy", () => {
|
||||||
test.each([
|
test.each([booked, empty, alternating])(
|
||||||
booked,
|
"getCompleteOccupancy of %j",
|
||||||
empty,
|
(occupancy) => {
|
||||||
alternating
|
|
||||||
])("getCompleteOccupancy of %j", (occupancy) => {
|
|
||||||
// arrange
|
// arrange
|
||||||
const startTime = new Date(testList.start);
|
const startTime = new Date(testList.start);
|
||||||
const endTime = new Date(addHours(startTime, 32));
|
const endTime = new Date(addHours(startTime, 32));
|
||||||
const sliceInterval = interval(startTime, endTime);
|
const sliceInterval = interval(startTime, endTime);
|
||||||
|
|
||||||
// act
|
// act
|
||||||
const sliced = testList["sliceOccupancy"](
|
const sliced = testList["sliceOccupancy"](sliceInterval, occupancy);
|
||||||
sliceInterval,
|
|
||||||
occupancy
|
|
||||||
)
|
|
||||||
|
|
||||||
// assert
|
// assert
|
||||||
expect(sliced).toEqual({
|
expect(sliced).toEqual({
|
||||||
decodeSliceStart: startTime,
|
decodeSliceStart: startTime,
|
||||||
decodeSlice: new Uint8Array(occupancy),
|
decodeSlice: new Uint8Array(occupancy),
|
||||||
});
|
});
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
test("throws start out of bounds", () => {
|
test("throws start out of bounds", () => {
|
||||||
// arrange
|
// arrange
|
||||||
const startTime = new Date(subHours(testList.start,1));
|
const startTime = new Date(subHours(testList.start, 1));
|
||||||
const endTime = new Date(addHours(startTime, 32));
|
const endTime = new Date(addHours(startTime, 32));
|
||||||
const sliceInterval = interval(startTime, endTime);
|
const sliceInterval = interval(startTime, endTime);
|
||||||
|
|
||||||
// act and assert
|
// act and assert
|
||||||
expect(() => {
|
expect(() => {
|
||||||
testList["sliceOccupancy"](
|
testList["sliceOccupancy"](sliceInterval, alternating);
|
||||||
sliceInterval,
|
|
||||||
alternating
|
|
||||||
)
|
|
||||||
}).toThrowError();
|
}).toThrowError();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -204,10 +192,7 @@ describe("RoomOccupancyList", () => {
|
|||||||
|
|
||||||
// act and assert
|
// act and assert
|
||||||
expect(() => {
|
expect(() => {
|
||||||
testList["sliceOccupancy"](
|
testList["sliceOccupancy"](sliceInterval, alternating);
|
||||||
sliceInterval,
|
|
||||||
alternating
|
|
||||||
)
|
|
||||||
}).toThrowError();
|
}).toThrowError();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -218,10 +203,7 @@ describe("RoomOccupancyList", () => {
|
|||||||
const sliceInterval = interval(startTime, endTime);
|
const sliceInterval = interval(startTime, endTime);
|
||||||
|
|
||||||
// act
|
// act
|
||||||
const sliced = testList["sliceOccupancy"](
|
const sliced = testList["sliceOccupancy"](sliceInterval, counting);
|
||||||
sliceInterval,
|
|
||||||
counting
|
|
||||||
)
|
|
||||||
|
|
||||||
// assert
|
// assert
|
||||||
expect(sliced).toEqual({
|
expect(sliced).toEqual({
|
||||||
@ -237,14 +219,11 @@ describe("RoomOccupancyList", () => {
|
|||||||
const sliceInterval = interval(startTime, endTime);
|
const sliceInterval = interval(startTime, endTime);
|
||||||
|
|
||||||
// act
|
// act
|
||||||
const sliced = testList["sliceOccupancy"](
|
const sliced = testList["sliceOccupancy"](sliceInterval, counting);
|
||||||
sliceInterval,
|
|
||||||
counting
|
|
||||||
)
|
|
||||||
|
|
||||||
// assert
|
// assert
|
||||||
expect(sliced).toEqual({
|
expect(sliced).toEqual({
|
||||||
decodeSliceStart: addHours(testListStart,8),
|
decodeSliceStart: addHours(testListStart, 8),
|
||||||
decodeSlice: new Uint8Array([0x01]),
|
decodeSlice: new Uint8Array([0x01]),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -257,7 +236,7 @@ describe("RoomOccupancyList", () => {
|
|||||||
start: testListStart,
|
start: testListStart,
|
||||||
granularity: 60,
|
granularity: 60,
|
||||||
blocks: 0,
|
blocks: 0,
|
||||||
rooms: []
|
rooms: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
// act
|
// act
|
||||||
@ -272,7 +251,9 @@ describe("RoomOccupancyList", () => {
|
|||||||
const testInterval = testList["getOccupancyInterval"]();
|
const testInterval = testList["getOccupancyInterval"]();
|
||||||
|
|
||||||
// assert
|
// assert
|
||||||
expect(testInterval).toEqual(interval(testListStart, addHours(testListStart, 32)));
|
expect(testInterval).toEqual(
|
||||||
|
interval(testListStart, addHours(testListStart, 32)),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -284,7 +265,7 @@ describe("RoomOccupancyList", () => {
|
|||||||
new Uint8Array([]),
|
new Uint8Array([]),
|
||||||
testListStart,
|
testListStart,
|
||||||
15,
|
15,
|
||||||
"Raum"
|
"Raum",
|
||||||
);
|
);
|
||||||
|
|
||||||
// assert
|
// assert
|
||||||
@ -298,7 +279,7 @@ describe("RoomOccupancyList", () => {
|
|||||||
booked,
|
booked,
|
||||||
testListStart,
|
testListStart,
|
||||||
15,
|
15,
|
||||||
"BOOKED"
|
"BOOKED",
|
||||||
);
|
);
|
||||||
|
|
||||||
// assert
|
// assert
|
||||||
@ -308,8 +289,8 @@ describe("RoomOccupancyList", () => {
|
|||||||
end: addHours(testListStart, 8).toISOString(),
|
end: addHours(testListStart, 8).toISOString(),
|
||||||
rooms: "BOOKED",
|
rooms: "BOOKED",
|
||||||
free: false,
|
free: false,
|
||||||
stub: false
|
stub: false,
|
||||||
}
|
},
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -320,7 +301,7 @@ describe("RoomOccupancyList", () => {
|
|||||||
empty,
|
empty,
|
||||||
testListStart,
|
testListStart,
|
||||||
15,
|
15,
|
||||||
"BOOKED"
|
"BOOKED",
|
||||||
);
|
);
|
||||||
|
|
||||||
// assert
|
// assert
|
||||||
@ -334,7 +315,7 @@ describe("RoomOccupancyList", () => {
|
|||||||
alternating,
|
alternating,
|
||||||
new Date("2024-01-01T00:00:00Z"),
|
new Date("2024-01-01T00:00:00Z"),
|
||||||
15,
|
15,
|
||||||
"ALTERNATING"
|
"ALTERNATING",
|
||||||
);
|
);
|
||||||
|
|
||||||
// assert
|
// assert
|
||||||
@ -344,29 +325,29 @@ describe("RoomOccupancyList", () => {
|
|||||||
end: "2024-01-01T01:00:00.000Z",
|
end: "2024-01-01T01:00:00.000Z",
|
||||||
rooms: "ALTERNATING",
|
rooms: "ALTERNATING",
|
||||||
free: false,
|
free: false,
|
||||||
stub: false
|
stub: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
start: "2024-01-01T02:00:00.000Z",
|
start: "2024-01-01T02:00:00.000Z",
|
||||||
end: "2024-01-01T03:00:00.000Z",
|
end: "2024-01-01T03:00:00.000Z",
|
||||||
rooms: "ALTERNATING",
|
rooms: "ALTERNATING",
|
||||||
free: false,
|
free: false,
|
||||||
stub: false
|
stub: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
start: "2024-01-01T04:00:00.000Z",
|
start: "2024-01-01T04:00:00.000Z",
|
||||||
end: "2024-01-01T05:00:00.000Z",
|
end: "2024-01-01T05:00:00.000Z",
|
||||||
rooms: "ALTERNATING",
|
rooms: "ALTERNATING",
|
||||||
free: false,
|
free: false,
|
||||||
stub: false
|
stub: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
start: "2024-01-01T06:00:00.000Z",
|
start: "2024-01-01T06:00:00.000Z",
|
||||||
end: "2024-01-01T07:00:00.000Z",
|
end: "2024-01-01T07:00:00.000Z",
|
||||||
rooms: "ALTERNATING",
|
rooms: "ALTERNATING",
|
||||||
free: false,
|
free: false,
|
||||||
stub: false
|
stub: false,
|
||||||
}
|
},
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -381,7 +362,7 @@ describe("RoomOccupancyList", () => {
|
|||||||
const stubEvents = RoomOccupancyList["generateStubEvents"](
|
const stubEvents = RoomOccupancyList["generateStubEvents"](
|
||||||
"ROOM",
|
"ROOM",
|
||||||
startTime,
|
startTime,
|
||||||
endTime
|
endTime,
|
||||||
);
|
);
|
||||||
|
|
||||||
// assert
|
// assert
|
||||||
@ -397,7 +378,7 @@ describe("RoomOccupancyList", () => {
|
|||||||
const stubEvents = RoomOccupancyList["generateStubEvents"](
|
const stubEvents = RoomOccupancyList["generateStubEvents"](
|
||||||
"ROOM",
|
"ROOM",
|
||||||
startTime,
|
startTime,
|
||||||
endTime
|
endTime,
|
||||||
);
|
);
|
||||||
|
|
||||||
// assert
|
// assert
|
||||||
@ -413,7 +394,7 @@ describe("RoomOccupancyList", () => {
|
|||||||
const stubEvents = RoomOccupancyList["generateStubEvents"](
|
const stubEvents = RoomOccupancyList["generateStubEvents"](
|
||||||
"ROOM",
|
"ROOM",
|
||||||
startTime,
|
startTime,
|
||||||
endTime
|
endTime,
|
||||||
);
|
);
|
||||||
|
|
||||||
// assert
|
// assert
|
||||||
@ -466,7 +447,7 @@ describe("RoomOccupancyList", () => {
|
|||||||
rooms: "ROOM",
|
rooms: "ROOM",
|
||||||
free: true,
|
free: true,
|
||||||
stub: true,
|
stub: true,
|
||||||
}
|
},
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -479,7 +460,7 @@ describe("RoomOccupancyList", () => {
|
|||||||
const stubEvents = RoomOccupancyList["generateStubEvents"](
|
const stubEvents = RoomOccupancyList["generateStubEvents"](
|
||||||
"ROOM",
|
"ROOM",
|
||||||
startTime,
|
startTime,
|
||||||
endTime
|
endTime,
|
||||||
);
|
);
|
||||||
|
|
||||||
// assert
|
// assert
|
||||||
@ -490,7 +471,7 @@ describe("RoomOccupancyList", () => {
|
|||||||
rooms: "ROOM",
|
rooms: "ROOM",
|
||||||
free: true,
|
free: true,
|
||||||
stub: true,
|
stub: true,
|
||||||
}
|
},
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -501,10 +482,13 @@ describe("RoomOccupancyList", () => {
|
|||||||
const startTime = new Date("2022-01-01T20:00:00Z");
|
const startTime = new Date("2022-01-01T20:00:00Z");
|
||||||
|
|
||||||
// act
|
// act
|
||||||
const shiftedTime = RoomOccupancyList["shiftTimeForwardInsideWorkday"](startTime);
|
const shiftedTime =
|
||||||
|
RoomOccupancyList["shiftTimeForwardInsideWorkday"](startTime);
|
||||||
|
|
||||||
// assert
|
// assert
|
||||||
expect(toZonedTime(shiftedTime, localTimezone)).toEqual(new Date("2022-01-02T00:00:00Z"));
|
expect(toZonedTime(shiftedTime, localTimezone)).toEqual(
|
||||||
|
new Date("2022-01-02T00:00:00Z"),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("don't shift time on the same day", () => {
|
test("don't shift time on the same day", () => {
|
||||||
@ -512,10 +496,13 @@ describe("RoomOccupancyList", () => {
|
|||||||
const startTime = new Date("2022-01-02T01:00:00Z");
|
const startTime = new Date("2022-01-02T01:00:00Z");
|
||||||
|
|
||||||
// act
|
// act
|
||||||
const shiftedTime = RoomOccupancyList["shiftTimeForwardInsideWorkday"](startTime);
|
const shiftedTime =
|
||||||
|
RoomOccupancyList["shiftTimeForwardInsideWorkday"](startTime);
|
||||||
|
|
||||||
// assert
|
// assert
|
||||||
expect(toZonedTime(shiftedTime, localTimezone)).toEqual(new Date("2022-01-02T02:00:00Z"));
|
expect(toZonedTime(shiftedTime, localTimezone)).toEqual(
|
||||||
|
new Date("2022-01-02T02:00:00Z"),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("don't shift if already inside workday", () => {
|
test("don't shift if already inside workday", () => {
|
||||||
@ -523,10 +510,13 @@ describe("RoomOccupancyList", () => {
|
|||||||
const startTime = new Date("2022-01-02T12:30:00Z");
|
const startTime = new Date("2022-01-02T12:30:00Z");
|
||||||
|
|
||||||
// act
|
// act
|
||||||
const shiftedTime = RoomOccupancyList["shiftTimeForwardInsideWorkday"](startTime);
|
const shiftedTime =
|
||||||
|
RoomOccupancyList["shiftTimeForwardInsideWorkday"](startTime);
|
||||||
|
|
||||||
// assert
|
// assert
|
||||||
expect(toZonedTime(shiftedTime, localTimezone)).toEqual(new Date("2022-01-02T13:30:00Z"));
|
expect(toZonedTime(shiftedTime, localTimezone)).toEqual(
|
||||||
|
new Date("2022-01-02T13:30:00Z"),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -536,10 +526,13 @@ describe("RoomOccupancyList", () => {
|
|||||||
const startTime = new Date("2022-01-02T05:30:00Z");
|
const startTime = new Date("2022-01-02T05:30:00Z");
|
||||||
|
|
||||||
// act
|
// act
|
||||||
const shiftedTime = RoomOccupancyList["shiftTimeBackwardInsideWorkday"](startTime);
|
const shiftedTime =
|
||||||
|
RoomOccupancyList["shiftTimeBackwardInsideWorkday"](startTime);
|
||||||
|
|
||||||
// assert
|
// assert
|
||||||
expect(toZonedTime(shiftedTime, localTimezone)).toEqual(new Date("2022-01-01T23:59:59.999Z"));
|
expect(toZonedTime(shiftedTime, localTimezone)).toEqual(
|
||||||
|
new Date("2022-01-01T23:59:59.999Z"),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("don't shift time on the same day", () => {
|
test("don't shift time on the same day", () => {
|
||||||
@ -547,10 +540,13 @@ describe("RoomOccupancyList", () => {
|
|||||||
const startTime = new Date("2022-01-02T22:00:00Z");
|
const startTime = new Date("2022-01-02T22:00:00Z");
|
||||||
|
|
||||||
// act
|
// act
|
||||||
const shiftedTime = RoomOccupancyList["shiftTimeBackwardInsideWorkday"](startTime);
|
const shiftedTime =
|
||||||
|
RoomOccupancyList["shiftTimeBackwardInsideWorkday"](startTime);
|
||||||
|
|
||||||
// assert
|
// assert
|
||||||
expect(toZonedTime(shiftedTime, localTimezone)).toEqual(new Date("2022-01-02T23:00:00Z"));
|
expect(toZonedTime(shiftedTime, localTimezone)).toEqual(
|
||||||
|
new Date("2022-01-02T23:00:00Z"),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("don't shift if already inside workday", () => {
|
test("don't shift if already inside workday", () => {
|
||||||
@ -558,10 +554,13 @@ describe("RoomOccupancyList", () => {
|
|||||||
const startTime = new Date("2022-01-02T12:30:00Z");
|
const startTime = new Date("2022-01-02T12:30:00Z");
|
||||||
|
|
||||||
// act
|
// act
|
||||||
const shiftedTime = RoomOccupancyList["shiftTimeBackwardInsideWorkday"](startTime);
|
const shiftedTime =
|
||||||
|
RoomOccupancyList["shiftTimeBackwardInsideWorkday"](startTime);
|
||||||
|
|
||||||
// assert
|
// assert
|
||||||
expect(toZonedTime(shiftedTime, localTimezone)).toEqual(new Date("2022-01-02T13:30:00Z"));
|
expect(toZonedTime(shiftedTime, localTimezone)).toEqual(
|
||||||
|
new Date("2022-01-02T13:30:00Z"),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -582,7 +581,11 @@ describe("RoomOccupancyList", () => {
|
|||||||
const startTime = new Date("2022-06-02T12:30:00Z");
|
const startTime = new Date("2022-06-02T12:30:00Z");
|
||||||
|
|
||||||
// act
|
// act
|
||||||
const shiftedTime = RoomOccupancyList["setTimeOfDay"](startTime, {hours: 23, minutes: 59, seconds: 59});
|
const shiftedTime = RoomOccupancyList["setTimeOfDay"](startTime, {
|
||||||
|
hours: 23,
|
||||||
|
minutes: 59,
|
||||||
|
seconds: 59,
|
||||||
|
});
|
||||||
|
|
||||||
// assert
|
// assert
|
||||||
expect(shiftedTime).toEqual(new Date("2022-06-02T21:59:59Z"));
|
expect(shiftedTime).toEqual(new Date("2022-06-02T21:59:59Z"));
|
||||||
@ -593,7 +596,11 @@ describe("RoomOccupancyList", () => {
|
|||||||
const startTime = new Date("2022-01-02T12:30:00Z");
|
const startTime = new Date("2022-01-02T12:30:00Z");
|
||||||
|
|
||||||
// act
|
// act
|
||||||
const shiftedTime = RoomOccupancyList["setTimeOfDay"](startTime, {hours: 13, minutes: 30, seconds: 0});
|
const shiftedTime = RoomOccupancyList["setTimeOfDay"](startTime, {
|
||||||
|
hours: 13,
|
||||||
|
minutes: 30,
|
||||||
|
seconds: 0,
|
||||||
|
});
|
||||||
|
|
||||||
// assert
|
// assert
|
||||||
expect(shiftedTime).toEqual(new Date("2022-01-02T12:30:00Z"));
|
expect(shiftedTime).toEqual(new Date("2022-01-02T12:30:00Z"));
|
||||||
@ -647,5 +654,4 @@ describe("RoomOccupancyList", () => {
|
|||||||
expect(shiftedTime).toEqual(new Date("2022-06-01:22:00Z"));
|
expect(shiftedTime).toEqual(new Date("2022-06-01:22:00Z"));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -16,13 +16,31 @@
|
|||||||
|
|
||||||
import { Binary } from "bson";
|
import { Binary } from "bson";
|
||||||
import { AnonymizedOccupancy } from "./event";
|
import { AnonymizedOccupancy } from "./event";
|
||||||
import { Duration, NormalizedInterval, add, addDays, addMinutes, clamp, differenceInMinutes, eachDayOfInterval, endOfDay, interval, isAfter, isBefore, isEqual, max, min, startOfDay, subDays } from "date-fns";
|
import {
|
||||||
|
Duration,
|
||||||
|
NormalizedInterval,
|
||||||
|
add,
|
||||||
|
addDays,
|
||||||
|
addMinutes,
|
||||||
|
clamp,
|
||||||
|
differenceInMinutes,
|
||||||
|
eachDayOfInterval,
|
||||||
|
endOfDay,
|
||||||
|
interval,
|
||||||
|
isAfter,
|
||||||
|
isBefore,
|
||||||
|
isEqual,
|
||||||
|
max,
|
||||||
|
min,
|
||||||
|
startOfDay,
|
||||||
|
subDays,
|
||||||
|
} from "date-fns";
|
||||||
import { fromZonedTime, toZonedTime } from "date-fns-tz";
|
import { fromZonedTime, toZonedTime } from "date-fns-tz";
|
||||||
|
|
||||||
/// The start time of the day. 07:00
|
/// The start time of the day. 07:00
|
||||||
const START_OF_WORKDAY : Duration = {hours: 7};
|
const START_OF_WORKDAY: Duration = { hours: 7 };
|
||||||
/// The end time of the day. 20:00
|
/// The end time of the day. 20:00
|
||||||
const END_OF_WORKDAY : Duration = {hours: 20};
|
const END_OF_WORKDAY: Duration = { hours: 20 };
|
||||||
/// The timezone of the data (Leipzig)
|
/// The timezone of the data (Leipzig)
|
||||||
const TIMEZONE = "Europe/Berlin";
|
const TIMEZONE = "Europe/Berlin";
|
||||||
|
|
||||||
@ -32,8 +50,8 @@ const TIMEZONE = "Europe/Berlin";
|
|||||||
*/
|
*/
|
||||||
class RoomOccupancy {
|
class RoomOccupancy {
|
||||||
constructor(
|
constructor(
|
||||||
public name : string,
|
public name: string,
|
||||||
public occupancy : Binary,
|
public occupancy: Binary,
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,17 +64,17 @@ class RoomOccupancy {
|
|||||||
*/
|
*/
|
||||||
export class RoomOccupancyList {
|
export class RoomOccupancyList {
|
||||||
constructor(
|
constructor(
|
||||||
public start : Date,
|
public start: Date,
|
||||||
public granularity : number,
|
public granularity: number,
|
||||||
public blocks : number,
|
public blocks: number,
|
||||||
public rooms : RoomOccupancy[],
|
public rooms: RoomOccupancy[],
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a list of all rooms encoded in this occupancy list.
|
* Get a list of all rooms encoded in this occupancy list.
|
||||||
* @returns a list of room names.
|
* @returns a list of room names.
|
||||||
*/
|
*/
|
||||||
public getRooms() : string[] {
|
public getRooms(): string[] {
|
||||||
return this.rooms.map((room) => room.name);
|
return this.rooms.map((room) => room.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,7 +85,11 @@ export class RoomOccupancyList {
|
|||||||
* @param to the end of the time range.
|
* @param to the end of the time range.
|
||||||
* @returns a list of AnonymizedEventDTO objects representing the occupancy of the room.
|
* @returns a list of AnonymizedEventDTO objects representing the occupancy of the room.
|
||||||
*/
|
*/
|
||||||
public decodeOccupancy(room : string, from : Date, to : Date) : AnonymizedOccupancy[] {
|
public decodeOccupancy(
|
||||||
|
room: string,
|
||||||
|
from: Date,
|
||||||
|
to: Date,
|
||||||
|
): AnonymizedOccupancy[] {
|
||||||
const roomOccupancy = this.rooms.find((r) => r.name === room);
|
const roomOccupancy = this.rooms.find((r) => r.name === room);
|
||||||
|
|
||||||
// Get start and end of decoded time range (within encoded list and requested range)
|
// Get start and end of decoded time range (within encoded list and requested range)
|
||||||
@ -82,19 +104,34 @@ export class RoomOccupancyList {
|
|||||||
|
|
||||||
let {decodeSliceStart, decodeSlice} = this.sliceOccupancy(
|
let {decodeSliceStart, decodeSlice} = this.sliceOccupancy(
|
||||||
decodeInterval,
|
decodeInterval,
|
||||||
roomOccupancy.occupancy.buffer
|
roomOccupancy.occupancy.buffer,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Decode the occupancy data
|
// Decode the occupancy data
|
||||||
occupancyList.push(...RoomOccupancyList.decodeOccupancyData(new Uint8Array(decodeSlice), decodeSliceStart, this.granularity, room));
|
occupancyList.push(
|
||||||
|
...RoomOccupancyList.decodeOccupancyData(
|
||||||
|
new Uint8Array(decodeSlice),
|
||||||
|
decodeSliceStart,
|
||||||
|
this.granularity,
|
||||||
|
room,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
// add stub events for the time before and after the decoded time range
|
// add stub events for the time before and after the decoded time range
|
||||||
if (!isEqual(from, decodeInterval.start)) {
|
if (!isEqual(from, decodeInterval.start)) {
|
||||||
occupancyList.push(...RoomOccupancyList.generateStubEvents(room, from, decodeInterval.start));
|
occupancyList.push(
|
||||||
|
...RoomOccupancyList.generateStubEvents(
|
||||||
|
room,
|
||||||
|
from,
|
||||||
|
decodeInterval.start,
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isEqual(to, decodeInterval.end)) {
|
if (!isEqual(to, decodeInterval.end)) {
|
||||||
occupancyList.push(...RoomOccupancyList.generateStubEvents(room, decodeInterval.end, to));
|
occupancyList.push(
|
||||||
|
...RoomOccupancyList.generateStubEvents(room, decodeInterval.end, to),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return occupancyList;
|
return occupancyList;
|
||||||
@ -107,10 +144,16 @@ export class RoomOccupancyList {
|
|||||||
* @returns a new occupancy byte array with the starting time of the first byte
|
* @returns a new occupancy byte array with the starting time of the first byte
|
||||||
* @throws an error, if the selected time range is outside of the occupancy list.
|
* @throws an error, if the selected time range is outside of the occupancy list.
|
||||||
*/
|
*/
|
||||||
private sliceOccupancy(decodeInterval : NormalizedInterval, occupancy : Uint8Array) : {decodeSliceStart: Date, decodeSlice: Uint8Array} {
|
private sliceOccupancy(
|
||||||
|
decodeInterval: NormalizedInterval,
|
||||||
|
occupancy: Uint8Array,
|
||||||
|
): { decodeSliceStart: Date; decodeSlice: Uint8Array } {
|
||||||
// Calculate the slice of bytes, that are needed to decode the requested time range
|
// Calculate the slice of bytes, that are needed to decode the requested time range
|
||||||
// Note: differenceInMinutes calculates (left - right)
|
// Note: differenceInMinutes calculates (left - right)
|
||||||
let minutesFromStart = differenceInMinutes(decodeInterval.start, this.start);
|
let minutesFromStart = differenceInMinutes(
|
||||||
|
decodeInterval.start,
|
||||||
|
this.start,
|
||||||
|
);
|
||||||
let minutesToEnd = differenceInMinutes(decodeInterval.end, this.start);
|
let minutesToEnd = differenceInMinutes(decodeInterval.end, this.start);
|
||||||
|
|
||||||
let firstByte = Math.floor(minutesFromStart / this.granularity / 8);
|
let firstByte = Math.floor(minutesFromStart / this.granularity / 8);
|
||||||
@ -118,13 +161,18 @@ export class RoomOccupancyList {
|
|||||||
|
|
||||||
// check if firstByte and lastByte are within the bounds of the occupancy array and throw an error if not
|
// check if firstByte and lastByte are within the bounds of the occupancy array and throw an error if not
|
||||||
if (
|
if (
|
||||||
firstByte < 0 || firstByte >= occupancy.length ||
|
firstByte < 0 ||
|
||||||
lastByte < 0 || lastByte > occupancy.length
|
firstByte >= occupancy.length ||
|
||||||
|
lastByte < 0 ||
|
||||||
|
lastByte > occupancy.length
|
||||||
) {
|
) {
|
||||||
throw new Error("Requested time range is outside of the occupancy list.");
|
throw new Error("Requested time range is outside of the occupancy list.");
|
||||||
}
|
}
|
||||||
|
|
||||||
let decodeSliceStart = addMinutes(this.start, firstByte * 8 * this.granularity);
|
let decodeSliceStart = addMinutes(
|
||||||
|
this.start,
|
||||||
|
firstByte * 8 * this.granularity,
|
||||||
|
);
|
||||||
let decodeSlice = occupancy.buffer.slice(firstByte, lastByte);
|
let decodeSlice = occupancy.buffer.slice(firstByte, lastByte);
|
||||||
|
|
||||||
return { decodeSliceStart, decodeSlice: new Uint8Array(decodeSlice) };
|
return { decodeSliceStart, decodeSlice: new Uint8Array(decodeSlice) };
|
||||||
@ -134,8 +182,11 @@ export class RoomOccupancyList {
|
|||||||
* Get the decoded time interval within the current occupancy list.
|
* Get the decoded time interval within the current occupancy list.
|
||||||
* @returns the interval of the occupancy list.
|
* @returns the interval of the occupancy list.
|
||||||
*/
|
*/
|
||||||
private getOccupancyInterval() : NormalizedInterval<Date> {
|
private getOccupancyInterval(): NormalizedInterval<Date> {
|
||||||
return interval(this.start, addMinutes(this.start, this.granularity * this.blocks));
|
return interval(
|
||||||
|
this.start,
|
||||||
|
addMinutes(this.start, this.granularity * this.blocks),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -146,9 +197,14 @@ export class RoomOccupancyList {
|
|||||||
* @param room the room name.
|
* @param room the room name.
|
||||||
* @returns a list of AnonymizedOccupancy objects representing the occupancy of the room.
|
* @returns a list of AnonymizedOccupancy objects representing the occupancy of the room.
|
||||||
*/
|
*/
|
||||||
public static decodeOccupancyData(occupancy : Uint8Array, start : Date, granularity : number, room : string) : AnonymizedOccupancy[] {
|
public static decodeOccupancyData(
|
||||||
|
occupancy: Uint8Array,
|
||||||
|
start: Date,
|
||||||
|
granularity: number,
|
||||||
|
room: string,
|
||||||
|
): AnonymizedOccupancy[] {
|
||||||
let occupancyList = [];
|
let occupancyList = [];
|
||||||
let firstOccupancyBit : number | null = null;
|
let firstOccupancyBit: number | null = null;
|
||||||
|
|
||||||
// Iterate over all bytes that are in the array
|
// Iterate over all bytes that are in the array
|
||||||
for (let byte_i = 0; byte_i < occupancy.length; byte_i++) {
|
for (let byte_i = 0; byte_i < occupancy.length; byte_i++) {
|
||||||
@ -156,7 +212,7 @@ export class RoomOccupancyList {
|
|||||||
|
|
||||||
// Iterate over all bits in the current byte
|
// Iterate over all bits in the current byte
|
||||||
for (let bit_i = 0; bit_i < 8; bit_i++) {
|
for (let bit_i = 0; bit_i < 8; bit_i++) {
|
||||||
let isOccupied = (byte & (1 << (7-bit_i))) !== 0;
|
let isOccupied = (byte & (1 << (7 - bit_i))) !== 0;
|
||||||
|
|
||||||
if (firstOccupancyBit === null && isOccupied) {
|
if (firstOccupancyBit === null && isOccupied) {
|
||||||
firstOccupancyBit = byte_i * 8 + bit_i;
|
firstOccupancyBit = byte_i * 8 + bit_i;
|
||||||
@ -165,13 +221,15 @@ export class RoomOccupancyList {
|
|||||||
let endTime = addMinutes(start, (byte_i * 8 + bit_i) * granularity);
|
let endTime = addMinutes(start, (byte_i * 8 + bit_i) * granularity);
|
||||||
|
|
||||||
// add event between start and end of a block of boolean true values
|
// add event between start and end of a block of boolean true values
|
||||||
occupancyList.push(new AnonymizedOccupancy(
|
occupancyList.push(
|
||||||
|
new AnonymizedOccupancy(
|
||||||
startTime.toISOString(),
|
startTime.toISOString(),
|
||||||
endTime.toISOString(),
|
endTime.toISOString(),
|
||||||
room,
|
room,
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
));
|
),
|
||||||
|
);
|
||||||
|
|
||||||
firstOccupancyBit = null;
|
firstOccupancyBit = null;
|
||||||
}
|
}
|
||||||
@ -183,13 +241,15 @@ export class RoomOccupancyList {
|
|||||||
let startTime = addMinutes(start, firstOccupancyBit * granularity);
|
let startTime = addMinutes(start, firstOccupancyBit * granularity);
|
||||||
let endTime = addMinutes(start, occupancy.length * 8 * granularity);
|
let endTime = addMinutes(start, occupancy.length * 8 * granularity);
|
||||||
|
|
||||||
occupancyList.push(new AnonymizedOccupancy(
|
occupancyList.push(
|
||||||
|
new AnonymizedOccupancy(
|
||||||
startTime.toISOString(),
|
startTime.toISOString(),
|
||||||
endTime.toISOString(),
|
endTime.toISOString(),
|
||||||
room,
|
room,
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
));
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return occupancyList;
|
return occupancyList;
|
||||||
@ -203,7 +263,11 @@ export class RoomOccupancyList {
|
|||||||
* @param to The end time within the specified end day.
|
* @param to The end time within the specified end day.
|
||||||
* @returns a list of AnonymizedEventDTO objects, from start to end.
|
* @returns a list of AnonymizedEventDTO objects, from start to end.
|
||||||
*/
|
*/
|
||||||
public static generateStubEvents(rooms : string, from : Date, to : Date) : AnonymizedOccupancy[] {
|
public static generateStubEvents(
|
||||||
|
rooms: string,
|
||||||
|
from: Date,
|
||||||
|
to: Date,
|
||||||
|
): AnonymizedOccupancy[] {
|
||||||
from = RoomOccupancyList.shiftTimeForwardInsideWorkday(from);
|
from = RoomOccupancyList.shiftTimeForwardInsideWorkday(from);
|
||||||
to = RoomOccupancyList.shiftTimeBackwardInsideWorkday(to);
|
to = RoomOccupancyList.shiftTimeBackwardInsideWorkday(to);
|
||||||
|
|
||||||
@ -211,9 +275,15 @@ export class RoomOccupancyList {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
return eachDayOfInterval({start: from, end: to}).map((day) => {
|
return eachDayOfInterval({ start: from, end: to }).map((day) => {
|
||||||
let startTime = max([from, RoomOccupancyList.setTimeOfDay(day, START_OF_WORKDAY)]);
|
let startTime = max([
|
||||||
let endTime = min([to, RoomOccupancyList.setTimeOfDay(day, END_OF_WORKDAY)]);
|
from,
|
||||||
|
RoomOccupancyList.setTimeOfDay(day, START_OF_WORKDAY),
|
||||||
|
]);
|
||||||
|
let endTime = min([
|
||||||
|
to,
|
||||||
|
RoomOccupancyList.setTimeOfDay(day, END_OF_WORKDAY),
|
||||||
|
]);
|
||||||
|
|
||||||
return new AnonymizedOccupancy(
|
return new AnonymizedOccupancy(
|
||||||
startTime.toISOString(),
|
startTime.toISOString(),
|
||||||
@ -231,13 +301,15 @@ export class RoomOccupancyList {
|
|||||||
* @param json the JS object to read from.
|
* @param json the JS object to read from.
|
||||||
* @returns a RoomOccupancyList object.
|
* @returns a RoomOccupancyList object.
|
||||||
*/
|
*/
|
||||||
public static fromJSON(json : any) : RoomOccupancyList {
|
public static fromJSON(json: any): RoomOccupancyList {
|
||||||
return new RoomOccupancyList(
|
return new RoomOccupancyList(
|
||||||
json.start,
|
json.start,
|
||||||
json.granularity,
|
json.granularity,
|
||||||
json.blocks,
|
json.blocks,
|
||||||
json.rooms.map((room : any) => new RoomOccupancy(room.name, room.occupancy)
|
json.rooms.map(
|
||||||
));
|
(room: any) => new RoomOccupancy(room.name, room.occupancy),
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -246,11 +318,11 @@ export class RoomOccupancyList {
|
|||||||
* @param date the date time to check if in bounds.
|
* @param date the date time to check if in bounds.
|
||||||
* @returns the shifted time.
|
* @returns the shifted time.
|
||||||
*/
|
*/
|
||||||
private static shiftTimeForwardInsideWorkday(date : Date) : Date {
|
private static shiftTimeForwardInsideWorkday(date: Date): Date {
|
||||||
// if the time of date is after the end of the workday
|
// if the time of date is after the end of the workday
|
||||||
if (isAfter(date, RoomOccupancyList.setTimeOfDay(date, END_OF_WORKDAY))) {
|
if (isAfter(date, RoomOccupancyList.setTimeOfDay(date, END_OF_WORKDAY))) {
|
||||||
// shift the time to the start of the next day
|
// shift the time to the start of the next day
|
||||||
return RoomOccupancyList.startOfDay(addDays(date,1));
|
return RoomOccupancyList.startOfDay(addDays(date, 1));
|
||||||
} else {
|
} else {
|
||||||
return date;
|
return date;
|
||||||
}
|
}
|
||||||
@ -262,11 +334,13 @@ export class RoomOccupancyList {
|
|||||||
* @param date the date time to check if in bounds.
|
* @param date the date time to check if in bounds.
|
||||||
* @returns the shifted time.
|
* @returns the shifted time.
|
||||||
*/
|
*/
|
||||||
private static shiftTimeBackwardInsideWorkday(date : Date) : Date {
|
private static shiftTimeBackwardInsideWorkday(date: Date): Date {
|
||||||
// if the time of date is before the start of the workday
|
// if the time of date is before the start of the workday
|
||||||
if (isBefore(date, RoomOccupancyList.setTimeOfDay(date, START_OF_WORKDAY))) {
|
if (
|
||||||
|
isBefore(date, RoomOccupancyList.setTimeOfDay(date, START_OF_WORKDAY))
|
||||||
|
) {
|
||||||
// shift the time to the end of the previous day
|
// shift the time to the end of the previous day
|
||||||
return RoomOccupancyList.endOfDay(subDays(date,1));
|
return RoomOccupancyList.endOfDay(subDays(date, 1));
|
||||||
} else {
|
} else {
|
||||||
return date;
|
return date;
|
||||||
}
|
}
|
||||||
@ -278,7 +352,7 @@ export class RoomOccupancyList {
|
|||||||
* @param time the time as Duration after 00:00.
|
* @param time the time as Duration after 00:00.
|
||||||
* @returns new date with changed time values.
|
* @returns new date with changed time values.
|
||||||
*/
|
*/
|
||||||
private static setTimeOfDay(date : Date, time : Duration) : Date {
|
private static setTimeOfDay(date: Date, time: Duration): Date {
|
||||||
return add(RoomOccupancyList.startOfDay(date), time);
|
return add(RoomOccupancyList.startOfDay(date), time);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -287,7 +361,7 @@ export class RoomOccupancyList {
|
|||||||
* @param date
|
* @param date
|
||||||
* @returns the start of the day.
|
* @returns the start of the day.
|
||||||
*/
|
*/
|
||||||
private static startOfDay(date : Date) : Date {
|
private static startOfDay(date: Date): Date {
|
||||||
const dateInLocalTimezone = toZonedTime(date, TIMEZONE);
|
const dateInLocalTimezone = toZonedTime(date, TIMEZONE);
|
||||||
return fromZonedTime(startOfDay(dateInLocalTimezone), TIMEZONE);
|
return fromZonedTime(startOfDay(dateInLocalTimezone), TIMEZONE);
|
||||||
}
|
}
|
||||||
@ -297,10 +371,8 @@ export class RoomOccupancyList {
|
|||||||
* @param date
|
* @param date
|
||||||
* @returns the end of the day.
|
* @returns the end of the day.
|
||||||
*/
|
*/
|
||||||
private static endOfDay(date : Date) : Date {
|
private static endOfDay(date: Date): Date {
|
||||||
const dateInLocalTimezone = toZonedTime(date, TIMEZONE);
|
const dateInLocalTimezone = toZonedTime(date, TIMEZONE);
|
||||||
return fromZonedTime(endOfDay(dateInLocalTimezone), TIMEZONE);
|
return fromZonedTime(endOfDay(dateInLocalTimezone), TIMEZONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,14 +29,23 @@ const EditModules = () => import("../view/editCalendar/EditModules.vue");
|
|||||||
const CourseSelection = () => import("../view/CourseSelection.vue");
|
const CourseSelection = () => import("../view/CourseSelection.vue");
|
||||||
const FreeRooms = () => import("../view/FreeRooms.vue");
|
const FreeRooms = () => import("../view/FreeRooms.vue");
|
||||||
const CalenderViewer = () => import("../view/UserCalendar.vue");
|
const CalenderViewer = () => import("../view/UserCalendar.vue");
|
||||||
|
const SettingsView = () => import("../view/SettingsView.vue");
|
||||||
|
const NotFound = () => import("../view/NotFound.vue");
|
||||||
|
|
||||||
import i18n from "../i18n";
|
import i18n from "../i18n";
|
||||||
|
import settingsStore from "@/store/settingsStore.ts";
|
||||||
|
|
||||||
|
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
history: createWebHistory(import.meta.env.BASE_URL),
|
history: createWebHistory(import.meta.env.BASE_URL),
|
||||||
routes: [
|
routes: [
|
||||||
{
|
{
|
||||||
path: "/",
|
path: "/",
|
||||||
|
name: "default",
|
||||||
|
component: CourseSelection,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/home",
|
||||||
name: "home",
|
name: "home",
|
||||||
component: CourseSelection,
|
component: CourseSelection,
|
||||||
},
|
},
|
||||||
@ -118,17 +127,35 @@ const router = createRouter({
|
|||||||
name: "rename-modules",
|
name: "rename-modules",
|
||||||
component: RenameModules,
|
component: RenameModules,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "/settings",
|
||||||
|
name: "settings",
|
||||||
|
component: SettingsView,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/:catchAll(.*)", // Catch all undefined routes
|
||||||
|
name: "not-found",
|
||||||
|
component: NotFound, // Replace with your NotFound component
|
||||||
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
router.beforeEach(async (to, from) => {
|
router.beforeEach(async (to, from, next) => {
|
||||||
const newLocale = to.params.locale;
|
const newLocale = to.params.locale;
|
||||||
const prevLocale = from.params.locale;
|
const prevLocale = from.params.locale;
|
||||||
// If the locale hasn't changed, do nothing
|
// If the locale hasn't changed, do nothing
|
||||||
if (newLocale === prevLocale) {
|
if (!(newLocale === prevLocale)) {
|
||||||
return;
|
|
||||||
}
|
|
||||||
i18n.setLocale(newLocale);
|
i18n.setLocale(newLocale);
|
||||||
|
}
|
||||||
|
|
||||||
|
const userSettings = settingsStore();
|
||||||
|
const defaultPath = userSettings.defaultPage || "/home";
|
||||||
|
|
||||||
|
if (to.path === "/") {
|
||||||
|
next(defaultPath.value);
|
||||||
|
} else {
|
||||||
|
next();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -16,18 +16,54 @@
|
|||||||
|
|
||||||
import { defineStore } from "pinia";
|
import { defineStore } from "pinia";
|
||||||
import { useLocalStorage } from "@vueuse/core";
|
import { useLocalStorage } from "@vueuse/core";
|
||||||
|
import router from "@/router";
|
||||||
|
|
||||||
const localeStore = defineStore("localeStore", {
|
const settingsStore = defineStore("settingsStore", {
|
||||||
state: () => {
|
state: () => {
|
||||||
return {
|
return {
|
||||||
locale: useLocalStorage("locale", "en"), //useLocalStorage takes in a key of 'count' and default value of 0
|
locale: useLocalStorage("locale", "en"), //useLocalStorage takes in a key of 'count' and default value of 0
|
||||||
|
isDark: true,
|
||||||
|
defaultPage: useLocalStorage("defaultPage", {label: "Home", value: "/home"}),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
setLocale(locale: string) {
|
setLocale(locale: string) {
|
||||||
this.locale = locale;
|
this.locale = locale;
|
||||||
},
|
},
|
||||||
|
setDarkMode(isDark: boolean) {
|
||||||
|
this.isDark = isDark;
|
||||||
|
},
|
||||||
|
getDarkMode(): boolean {
|
||||||
|
return this.isDark;
|
||||||
|
},
|
||||||
|
setDefaultPage(page: {
|
||||||
|
label: string;
|
||||||
|
value: string;
|
||||||
|
}) {
|
||||||
|
this.defaultPage = page;
|
||||||
|
},
|
||||||
|
getDefaultPageOptions(): {
|
||||||
|
label: string;
|
||||||
|
value: string;
|
||||||
|
}[] {
|
||||||
|
// get a string array of all the route names
|
||||||
|
const options: {
|
||||||
|
label: string;
|
||||||
|
value: string;
|
||||||
|
}[] = [];
|
||||||
|
router.getRoutes().forEach((route) => {
|
||||||
|
if (route.name) {
|
||||||
|
if (typeof route.name === "string") {
|
||||||
|
options.push({
|
||||||
|
label: route.name,
|
||||||
|
value: route.path,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return options;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default localeStore;
|
export default settingsStore;
|
@ -47,9 +47,9 @@ const hasContent = computed(() => {
|
|||||||
class="flex align-items-center justify-content-center gap-3 mx-2 mb-4 transition-rolldown"
|
class="flex align-items-center justify-content-center gap-3 mx-2 mb-4 transition-rolldown"
|
||||||
:class="{ 'md:mt-8': hideContent }"
|
:class="{ 'md:mt-8': hideContent }"
|
||||||
>
|
>
|
||||||
<h3 class="text-4xl">
|
<h1 class="text-4xl">
|
||||||
{{ headline }}
|
{{ headline }}
|
||||||
</h3>
|
</h1>
|
||||||
<i v-if="icon" :class="icon" style="font-size: 2rem"></i>
|
<i v-if="icon" :class="icon" style="font-size: 2rem"></i>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="subTitle" class="flex justify-content-center">
|
<div v-if="subTitle" class="flex justify-content-center">
|
||||||
|
@ -129,8 +129,12 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template #body="slotProps">
|
<template #body="slotProps">
|
||||||
<div class="flex flex-column sm:flex-row justify-content-between flex-1 column-gap-4 mx-2 md:mx-4">
|
<div
|
||||||
<p class="flex-1 align-self-stretch sm:align-self-center my-2">{{ slotProps.data.room }}</p>
|
class="flex flex-column sm:flex-row justify-content-between flex-1 column-gap-4 mx-2 md:mx-4"
|
||||||
|
>
|
||||||
|
<p class="flex-1 align-self-stretch sm:align-self-center my-2">
|
||||||
|
{{ slotProps.data.room }}
|
||||||
|
</p>
|
||||||
<Button
|
<Button
|
||||||
:label="$t('freeRooms.viewOccupancy')"
|
:label="$t('freeRooms.viewOccupancy')"
|
||||||
icon="pi pi-hourglass"
|
icon="pi pi-hourglass"
|
||||||
|
16
frontend/src/view/NotFound.vue
Normal file
16
frontend/src/view/NotFound.vue
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import DynamicPage from "@/view/DynamicPage.vue";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<DynamicPage
|
||||||
|
hide-content
|
||||||
|
:headline="$t('notFound.headline')"
|
||||||
|
:sub-title="$t('notFound.subTitle')"
|
||||||
|
>
|
||||||
|
</DynamicPage>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
78
frontend/src/view/SettingsView.vue
Normal file
78
frontend/src/view/SettingsView.vue
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
|
||||||
|
import LocaleSwitcher from "@/components/LocaleSwitcher.vue";
|
||||||
|
import { ref } from "vue";
|
||||||
|
import DarkModeSwitcher from "@/components/DarkModeSwitcher.vue";
|
||||||
|
import DefaultPageSwitcher from "@/components/DefaultPageSwitcher.vue";
|
||||||
|
|
||||||
|
const icon = "pi pi-cog";
|
||||||
|
const isDark = ref(true);
|
||||||
|
|
||||||
|
function handleDarkModeToggled(isDarkVar: boolean) {
|
||||||
|
// Do something with isDark value
|
||||||
|
// For example, update the root isDark value
|
||||||
|
// Assuming the root component has an isDark ref
|
||||||
|
isDark.value = isDarkVar;
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="flex flex-column align-items-center mt-0">
|
||||||
|
<div
|
||||||
|
class="flex align-items-center justify-content-center gap-3 mx-2 mb-4 transition-rolldown md:mt-8"
|
||||||
|
>
|
||||||
|
<h1 class="text-4xl">
|
||||||
|
{{ $t("settings.headline") }}
|
||||||
|
</h1>
|
||||||
|
<i v-if="icon" :class="icon" style="font-size: 2rem"></i>
|
||||||
|
</div>
|
||||||
|
<div v-if="$t('settings.subTitle')" class="flex justify-content-center">
|
||||||
|
<h5 class="text-2xl m-2">{{ $t("settings.subTitle") }}</h5>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-wrap mx-0 gap-2 my-4 w-full lg:w-8">
|
||||||
|
<slot flex-specs="flex-1 m-0" name="selection"></slot>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="opacity-100 transition-all transition-duration-500 transition-ease-in-out w-full lg:w-8"
|
||||||
|
>
|
||||||
|
<div class="flex flex-column justify-content-center">
|
||||||
|
<div class="grid my-2">
|
||||||
|
<div class="col text-center">
|
||||||
|
{{ $t("settings.language") }}
|
||||||
|
</div>
|
||||||
|
<div class="col text-center">
|
||||||
|
<LocaleSwitcher />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="grid my-2">
|
||||||
|
<div class="col text-center">
|
||||||
|
{{ $t("settings.defaultPage") }}
|
||||||
|
</div>
|
||||||
|
<div class="col text-center">
|
||||||
|
<DefaultPageSwitcher />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="grid my-2">
|
||||||
|
<div class="col text-center">
|
||||||
|
{{ $t("settings.darkMode") }}
|
||||||
|
</div>
|
||||||
|
<div class="col text-center">
|
||||||
|
<DarkModeSwitcher
|
||||||
|
@dark-mode-toggled="handleDarkModeToggled"
|
||||||
|
></DarkModeSwitcher>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
.col {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
@ -1,5 +1,4 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
|
||||||
import CalendarViewer from "@/components/CalendarViewer.vue";
|
import CalendarViewer from "@/components/CalendarViewer.vue";
|
||||||
import DynamicPage from "@/view/DynamicPage.vue";
|
import DynamicPage from "@/view/DynamicPage.vue";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
@ -12,7 +11,7 @@ import tokenStore from "@/store/tokenStore.ts";
|
|||||||
const { t } = useI18n({ useScope: "global" });
|
const { t } = useI18n({ useScope: "global" });
|
||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
|
|
||||||
const token = ref(tokenStore().token || "" as string );
|
const token = ref(tokenStore().token || ("" as string));
|
||||||
|
|
||||||
// parse token from query parameter
|
// parse token from query parameter
|
||||||
const urlParams = new URLSearchParams(window.location.search);
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
@ -23,6 +22,8 @@ if (tokenFromUrl) {
|
|||||||
loadCalendar();
|
loadCalendar();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const calendarViewerRef = ref<InstanceType<typeof CalendarViewer>>();
|
||||||
|
|
||||||
function loadCalendar() {
|
function loadCalendar() {
|
||||||
try {
|
try {
|
||||||
token.value = extractToken(token.value);
|
token.value = extractToken(token.value);
|
||||||
@ -36,19 +37,26 @@ function loadCalendar() {
|
|||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
moduleStore().removeAllModules();
|
moduleStore().removeAllModules();
|
||||||
tokenStore().setToken(token.value);
|
tokenStore().setToken(token.value);
|
||||||
|
|
||||||
|
calendarViewerRef.value?.invalidateAndRefetchCalendar();
|
||||||
|
|
||||||
|
toast.add({
|
||||||
|
severity: "success",
|
||||||
|
summary: t("editCalendarView.toast.success"),
|
||||||
|
detail: t("editCalendarView.toast.successDetailLoad"),
|
||||||
|
life: 3000,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (token.value && token.value !== "") {
|
if (token.value && token.value !== "") {
|
||||||
loadCalendar();
|
//loadCalendar();
|
||||||
|
tokenStore().setToken(token.value);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<DynamicPage
|
<DynamicPage
|
||||||
:hide-content="false"
|
:hide-content="false"
|
||||||
@ -66,16 +74,12 @@ onMounted(() => {
|
|||||||
:label="$t('userCalender.searchButton')"
|
:label="$t('userCalender.searchButton')"
|
||||||
icon="pi pi-refresh"
|
icon="pi pi-refresh"
|
||||||
@click="loadCalendar()"
|
@click="loadCalendar()"
|
||||||
/>
|
></Button>
|
||||||
</template>
|
</template>
|
||||||
<template #content>
|
<template #content>
|
||||||
<CalendarViewer
|
<CalendarViewer :token="tokenStore().token" ref="calendarViewerRef" />
|
||||||
:token="tokenStore().token"
|
|
||||||
/>
|
|
||||||
</template>
|
</template>
|
||||||
</DynamicPage>
|
</DynamicPage>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped></style>
|
||||||
|
|
||||||
</style>
|
|
||||||
|
@ -34,9 +34,9 @@ async function nextStep() {
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex flex-column align-items-center w-full mb-7">
|
<div class="flex flex-column align-items-center w-full mb-7">
|
||||||
<div class="flex align-items-center justify-content-center m-2">
|
<div class="flex align-items-center justify-content-center m-2">
|
||||||
<h3>
|
<h1>
|
||||||
{{ $t("additionalModules.subTitle") }}
|
{{ $t("additionalModules.subTitle") }}
|
||||||
</h3>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
<AdditionalModuleTable />
|
<AdditionalModuleTable />
|
||||||
<div
|
<div
|
||||||
|
2
frontend/src/vite-env.d.ts
vendored
2
frontend/src/vite-env.d.ts
vendored
@ -16,4 +16,4 @@
|
|||||||
|
|
||||||
/// <reference types="vite/client" />
|
/// <reference types="vite/client" />
|
||||||
|
|
||||||
declare module 'ical.js';
|
declare module "ical.js";
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
"allowSyntheticDefaultImports": true,
|
"allowSyntheticDefaultImports": true,
|
||||||
"paths": {
|
"paths": {
|
||||||
"@/*": ["./src/*"]
|
"@/*": ["./src/*"]
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
|
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
|
||||||
"references": [
|
"references": [
|
||||||
|
@ -17,63 +17,90 @@
|
|||||||
import { defineConfig } from "vite";
|
import { defineConfig } from "vite";
|
||||||
import vue from "@vitejs/plugin-vue";
|
import vue from "@vitejs/plugin-vue";
|
||||||
import { fileURLToPath } from "node:url";
|
import { fileURLToPath } from "node:url";
|
||||||
import { VitePWA } from 'vite-plugin-pwa';
|
import { VitePWA } from "vite-plugin-pwa";
|
||||||
import basicSsl from '@vitejs/plugin-basic-ssl'
|
import basicSsl from "@vitejs/plugin-basic-ssl";
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [
|
plugins: [
|
||||||
vue(),
|
vue(),
|
||||||
basicSsl(),
|
basicSsl(),
|
||||||
VitePWA({
|
VitePWA({
|
||||||
mode: 'development',
|
mode: "development",
|
||||||
base: '/',
|
base: "/",
|
||||||
injectRegister: 'auto',
|
injectRegister: "auto",
|
||||||
includeAssets: ['favicon.ico', 'apple-touch-icon.png', 'mask-icon.svg'],
|
includeAssets: ["favicon.ico", "apple-touch-icon.png", "mask-icon.svg"],
|
||||||
manifest: {
|
manifest: {
|
||||||
name: 'HTWKalender',
|
name: "HTWKalender",
|
||||||
short_name: 'HTWKalender',
|
short_name: "HTWKalender",
|
||||||
description: 'Calendar implementation for the HTWK Leipzig timetable. Evaluation and display of the individual dates in iCal format.',
|
description:
|
||||||
theme_color: '#FFFFFF',
|
"Calendar implementation for the HTWK Leipzig timetable. Evaluation and display of the individual dates in iCal format.",
|
||||||
background_color: '#FFFFFF',
|
theme_color: "#FFFFFF",
|
||||||
display: 'standalone',
|
background_color: "#FFFFFF",
|
||||||
start_url: '/',
|
display: "standalone",
|
||||||
|
start_url: "/",
|
||||||
|
id: "de.htwk-leipzig.htwkalender",
|
||||||
|
screenshots: [
|
||||||
|
{
|
||||||
|
src: "/1280x720.png",
|
||||||
|
sizes: "1280x720",
|
||||||
|
form_factor: "wide",
|
||||||
|
type: "image/png",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: "/390x844.png",
|
||||||
|
sizes: "1170x2532",
|
||||||
|
form_factor: "narrow",
|
||||||
|
type: "image/png",
|
||||||
|
},
|
||||||
|
],
|
||||||
icons: [
|
icons: [
|
||||||
{
|
{
|
||||||
src: "/pwa-192x192.png",
|
src: "/pwa-192x192.png",
|
||||||
sizes: "192x192",
|
sizes: "192x192",
|
||||||
type: "image/png",
|
type: "image/png",
|
||||||
purpose: "any"
|
purpose: "any",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
src: "/pwa-512x512.png",
|
src: "/pwa-512x512.png",
|
||||||
sizes: "512x512",
|
sizes: "512x512",
|
||||||
type: "image/png",
|
type: "image/png",
|
||||||
purpose: "any"
|
purpose: "any",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
src: "/pwa-maskable-192x192.png",
|
src: "/pwa-maskable-192x192.png",
|
||||||
sizes: "192x192",
|
sizes: "192x192",
|
||||||
type: "image/png",
|
type: "image/png",
|
||||||
purpose: "maskable"
|
purpose: "maskable",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
src: "/pwa-maskable-512x512.png",
|
src: "/pwa-maskable-512x512.png",
|
||||||
sizes: "512x512",
|
sizes: "512x512",
|
||||||
type: "image/png",
|
type: "image/png",
|
||||||
purpose: "maskable"
|
purpose: "maskable",
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
registerType: 'autoUpdate',
|
registerType: "autoUpdate",
|
||||||
workbox: {
|
workbox: {
|
||||||
globPatterns: ['**/*.{js,css,html,ico,png,svg,json,vue,txt,woff2}'],
|
globPatterns: ["**/*.{js,css,html,ico,png,svg,json,vue,txt,woff2}"],
|
||||||
cleanupOutdatedCaches: true,
|
cleanupOutdatedCaches: true,
|
||||||
runtimeCaching: [
|
runtimeCaching: [
|
||||||
{
|
{
|
||||||
urlPattern: /^https?.*/,
|
urlPattern: ({ url }) => url.pathname.startsWith('/api/feed'),
|
||||||
|
method: 'GET',
|
||||||
handler: 'NetworkFirst',
|
handler: 'NetworkFirst',
|
||||||
options: {
|
options: {
|
||||||
cacheName: 'https-calls',
|
cacheName: 'calendar-feed-cache',
|
||||||
|
expiration: {
|
||||||
|
maxAgeSeconds: 12 * 60 * 60, // 12 hours
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
urlPattern: /^https?.*/,
|
||||||
|
handler: "NetworkFirst",
|
||||||
|
options: {
|
||||||
|
cacheName: "https-calls",
|
||||||
expiration: {
|
expiration: {
|
||||||
maxEntries: 150,
|
maxEntries: 150,
|
||||||
maxAgeSeconds: 30 * 12 * 60 * 60, // 1 month
|
maxAgeSeconds: 30 * 12 * 60 * 60, // 1 month
|
||||||
@ -86,11 +113,12 @@ export default defineConfig({
|
|||||||
devOptions: {
|
devOptions: {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
/* when using generateSW the PWA plugin will switch to classic */
|
/* when using generateSW the PWA plugin will switch to classic */
|
||||||
type: 'module',
|
type: "module",
|
||||||
navigateFallback: 'index.html',
|
navigateFallback: "index.html",
|
||||||
suppressWarnings: true,
|
suppressWarnings: true,
|
||||||
}
|
},
|
||||||
})],
|
}),
|
||||||
|
],
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
"@": fileURLToPath(new URL("./src", import.meta.url)),
|
"@": fileURLToPath(new URL("./src", import.meta.url)),
|
||||||
@ -112,14 +140,14 @@ export default defineConfig({
|
|||||||
},
|
},
|
||||||
esbuild: {
|
esbuild: {
|
||||||
supported: {
|
supported: {
|
||||||
'top-level-await': true
|
"top-level-await": true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
optimizeDeps: {
|
optimizeDeps: {
|
||||||
esbuildOptions: {
|
esbuildOptions: {
|
||||||
supported: {
|
supported: {
|
||||||
'top-level-await': true
|
"top-level-await": true,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -1,11 +1,14 @@
|
|||||||
// vitest.config.ts
|
// vitest.config.ts
|
||||||
import {mergeConfig} from 'vite'
|
import { mergeConfig } from "vite";
|
||||||
import {defineConfig} from 'vitest/config'
|
import { defineConfig } from "vitest/config";
|
||||||
import viteConfig from './vite.config'
|
import viteConfig from "./vite.config";
|
||||||
|
|
||||||
export default mergeConfig(viteConfig, defineConfig({
|
export default mergeConfig(
|
||||||
|
viteConfig,
|
||||||
|
defineConfig({
|
||||||
test: {
|
test: {
|
||||||
globals: true,
|
globals: true,
|
||||||
globalSetup: './vitest.global-setup.ts',
|
globalSetup: "./vitest.global-setup.ts",
|
||||||
},
|
},
|
||||||
}))
|
}),
|
||||||
|
);
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
export const setup = () => {
|
export const setup = () => {
|
||||||
process.env.TZ = 'UTC'
|
process.env.TZ = "UTC";
|
||||||
}
|
};
|
||||||
|
Reference in New Issue
Block a user