Add Timepoll availability polling application and image
Build custom container images / build (map[base_image:php:8-fpm-alpine build_args:PHP_VERSION=8 context:php8-pgsql fingerprint_command:{ apk info -v | LC_ALL=C sort; find /usr/local/lib/php/extensions /usr/local/etc/php/conf.d -type f -exec sha256sum {} + | LC_ALL=C sort; } name:ph… (push) Successful in 49s
Build custom container images / build (map[base_image:postgres:18 build_args:PG_VERSION=18 POSTGIS_VERSION=3 VCHORD_VERSION=0.5.3 context:postgres fingerprint_command:{ dpkg-query -W -f='${binary:Package}=${Version}\n' | LC_ALL=C sort; find /usr/lib/postgresql -type f -exec sha256su… (push) Successful in 1m42s
Build custom container images / build (map[base_image:python:3 build_args:PYTHON_VERSION=3 context:python-tools fingerprint_command:{ dpkg-query -W -f='${binary:Package}=${Version}\n' | LC_ALL=C sort; pip freeze | LC_ALL=C sort; } name:python-tools oci_labels:org.opencontainers.ima… (push) Successful in 54s
Build custom container images / build (map[base_image:python:3.12-slim build_args:PYTHON_VERSION=3.12-slim context:linkki-tiedotus fingerprint_command:{ dpkg-query -W -f='${binary:Package}=${Version}\n' | LC_ALL=C sort; pip freeze | LC_ALL=C sort; } name:linkki-tiedotus oci_labels:… (push) Successful in 29s
Build custom container images / build (map[base_image:python:3.12-slim build_args:PYTHON_VERSION=3.12-slim context:timepoll fingerprint_command:{ dpkg-query -W -f='${binary:Package}=${Version}\n' | LC_ALL=C sort; pip freeze | LC_ALL=C sort; } name:timepoll oci_labels:org.opencontai… (push) Successful in 44s

This commit is contained in:
ajp_anton
2026-09-09 19:39:40 +00:00
parent cb80d420c0
commit 4cc2e8fc80
21 changed files with 3519 additions and 2 deletions
+859
View File
@@ -0,0 +1,859 @@
:root {
color-scheme: light dark;
--bg: #fafcfb;
--surface: #fff;
--ink: #182a24;
--muted: #63736d;
--line: #d5dfd9;
--soft: #eef3ef;
--green: #166548;
--blue: #1254ad;
--selected: #dcece4;
--shadow: 0 8px 30px #18342625;
--slot-empty: #f1f4f2;
--stripe: #13241d75;
--gray: #68717c;
}
@media (prefers-color-scheme: dark) {
:root {
--bg: #131817;
--surface: #1b2320;
--ink: #ebf1ed;
--muted: #a4b3ac;
--line: #3a4841;
--soft: #222e27;
--green: #82d5ac;
--blue: #91c5ff;
--selected: #294237;
--shadow: 0 8px 30px #0007;
--slot-empty: #202c25;
--stripe: #e9fff39c;
--gray: #afb9c1;
}
}
* {
box-sizing: border-box;
letter-spacing: 0;
}
body {
margin: 0;
background: var(--bg);
color: var(--ink);
font:
15px/1.5 system-ui,
sans-serif;
}
button,
input,
select {
font: inherit;
}
button,
a,
input,
select {
touch-action: manipulation;
}
a {
color: var(--blue);
text-underline-offset: 3px;
}
button {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 7px;
min-height: 40px;
padding: 7px 13px;
border: 1px solid var(--line);
border-radius: 6px;
color: var(--ink);
background: var(--surface);
cursor: pointer;
}
button:hover {
background: var(--soft);
}
button:disabled {
cursor: default;
opacity: 0.5;
}
.primary {
background: #196b4c;
color: #fff;
border-color: #196b4c;
}
.primary:hover {
background: #13583e;
}
.button-link {
display: inline-flex;
align-items: center;
gap: 7px;
padding: 7px 13px;
min-height: 40px;
border-radius: 6px;
text-decoration: none;
}
.danger {
color: #ba3838;
}
.icon-button {
width: 40px;
padding: 8px;
flex: none;
}
svg.lucide {
width: 18px;
height: 18px;
flex: none;
}
input,
select {
min-height: 40px;
max-width: 100%;
padding: 7px 10px;
border: 1px solid var(--line);
border-radius: 5px;
background: var(--surface);
color: var(--ink);
}
input[type="checkbox"] {
min-height: auto;
accent-color: var(--green);
width: 17px;
height: 17px;
}
input[type="time"],
input[type="date"] {
width: auto;
}
input:focus,
select:focus,
button:focus-visible,
a:focus-visible {
outline: 2px solid var(--blue);
outline-offset: 3px;
}
label {
max-width: 100%;
min-width: 0;
display: flex;
flex-direction: column;
gap: 5px;
font-size: 14px;
font-weight: 550;
}
.check {
flex-direction: row;
align-items: center;
gap: 9px;
font-weight: 400;
}
.site-header {
height: 66px;
border-bottom: 1px solid var(--line);
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 28px;
}
.brand {
display: inline-flex;
align-items: center;
gap: 10px;
font-size: 21px;
font-weight: 700;
text-decoration: none;
color: var(--ink);
}
.brand img {
border-radius: 6px;
}
#connection {
color: var(--muted);
font-size: 13px;
}
main {
max-width: 1420px;
margin: auto;
padding: 26px 28px 60px;
}
h1 {
font-size: 28px;
line-height: 1.25;
margin: 0 0 12px;
overflow-wrap: anywhere;
}
h2 {
font-size: 19px;
line-height: 1.35;
margin: 0 0 14px;
}
h3 {
font-size: 16px;
margin: 0 0 10px;
}
p {
margin: 8px 0 16px;
}
.muted {
color: var(--muted);
}
.home {
max-width: 800px;
margin: 40px auto;
}
.home h1 {
font-size: 36px;
}
.home-actions {
display: grid;
grid-template-columns: 1fr 1fr;
margin-top: 32px;
gap: 40px;
border-top: 1px solid var(--line);
padding-top: 28px;
}
.home-actions section + section {
border-left: 1px solid var(--line);
padding-left: 40px;
}
.home-actions form {
display: flex;
gap: 8px;
align-items: end;
flex-wrap: wrap;
}
.home-actions input {
width: 170px;
}
.heading {
display: flex;
justify-content: space-between;
align-items: start;
gap: 16px;
margin-bottom: 22px;
}
.heading h1 {
margin-bottom: 6px;
}
.row {
display: flex;
gap: 12px;
align-items: center;
flex-wrap: wrap;
}
.between {
justify-content: space-between;
}
.toolbar {
display: flex;
gap: 12px;
align-items: end;
flex-wrap: wrap;
margin: 18px 0;
}
.toolbar label {
font-weight: 400;
}
.fields {
display: flex;
flex-wrap: wrap;
gap: 18px;
align-items: end;
}
.fields .wide {
width: 320px;
max-width: 100%;
}
.editor {
max-width: 1150px;
margin: auto;
}
.band {
border-top: 1px solid var(--line);
padding: 22px 0;
}
.dates {
display: flex;
flex-wrap: wrap;
gap: 6px;
max-height: 150px;
overflow: auto;
margin-top: 16px;
}
.date-chip {
min-height: 32px;
padding: 3px 9px;
font-size: 13px;
}
.date-chip svg {
width: 13px;
height: 13px;
}
.date-entry {
display: flex;
align-items: end;
gap: 18px 30px;
flex-wrap: wrap;
}
.date-range-entry,
.single-date-entry {
display: flex;
flex-wrap: wrap;
align-items: end;
gap: 8px;
}
.date-bucket {
margin: 22px 0 0;
padding: 12px 14px 16px;
border: 1px solid var(--line);
border-radius: 5px;
min-width: 0;
background: var(--soft);
}
.date-bucket legend {
padding: 0 7px;
font-weight: 600;
}
.date-bucket .dates {
min-height: 38px;
margin: 0;
align-items: center;
}
#date-count {
margin-left: 8px;
font-size: 13px;
font-weight: 400;
}
.clipboard-buffer {
position: fixed;
left: -9999px;
top: 0;
}
.code {
font-family: ui-monospace, SFMono-Regular, Consolas, monospace;
white-space: nowrap;
font-size: 16px;
font-weight: 650;
}
.code .digit {
color: var(--blue);
}
.links {
max-width: 820px;
margin: auto;
}
.link-row {
display: flex;
gap: 8px;
align-items: center;
margin: 12px 0;
}
.link-row input {
flex: 1;
min-width: 0;
}
.links section {
padding: 22px 0;
border-top: 1px solid var(--line);
}
.links a.primary {
display: inline-flex;
border-radius: 6px;
padding: 10px 16px;
text-decoration: none;
}
.people {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 7px;
margin: 16px 0;
}
.person {
max-width: 100%;
overflow-wrap: anywhere;
min-height: 34px;
font-size: 14px;
}
.person.active {
background: var(--selected);
border-color: var(--green);
}
.person.filtered {
border-color: var(--gray);
background: var(--soft);
}
.new-person {
display: flex;
gap: 8px;
align-items: end;
flex-wrap: wrap;
}
.new-person input {
width: 240px;
}
.editbar {
display: flex;
align-items: center;
gap: 15px;
flex-wrap: wrap;
padding: 13px 0;
border-top: 1px solid var(--line);
border-bottom: 1px solid var(--line);
margin: 16px 0;
}
.editbar .save-actions {
margin-left: auto;
display: flex;
gap: 8px;
}
.editbar strong {
max-width: 100%;
overflow-wrap: anywhere;
}
#vote-actions {
margin-bottom: 18px;
}
.segmented {
display: inline-flex;
max-width: 100%;
flex-wrap: wrap;
}
.segmented button {
border-radius: 0;
margin-left: -1px;
}
.segmented button:first-child {
border-radius: 6px 0 0 6px;
margin-left: 0;
}
.segmented button:last-child {
border-radius: 0 6px 6px 0;
}
.segmented button[aria-pressed="true"] {
background: var(--selected);
border-color: var(--green);
position: relative;
}
.legend {
display: flex;
flex-wrap: wrap;
gap: 16px;
font-size: 13px;
color: var(--muted);
align-items: center;
}
.swatch {
display: inline-block;
width: 13px;
height: 13px;
margin-right: 5px;
border-radius: 2px;
vertical-align: -2px;
background: #399368;
}
.swatch.blue {
background: #478aca;
}
.swatch.maybe {
background:
repeating-linear-gradient(135deg, transparent 0 3px, #405e5070 3px 4px),
#8ac5a8;
}
.swatch.gray {
background: #949fa4;
}
.grid-scroll {
overflow: auto;
max-height: 72vh;
border: 1px solid var(--line);
border-radius: 5px;
background: var(--surface);
overscroll-behavior: contain;
position: relative;
scrollbar-gutter: stable;
}
.grid {
--day-width: 112px;
--time-width: 83px;
border-spacing: 0;
table-layout: fixed;
min-width: calc(var(--time-width) + var(--columns) * var(--day-width));
width: 100%;
border-collapse: separate;
}
.grid .time-column {
width: var(--time-width);
}
.grid th {
position: sticky;
top: 0;
z-index: 2;
background: var(--surface);
font-size: 13px;
text-align: center;
padding: 10px 8px;
border-bottom: 1px solid var(--line);
min-width: 112px;
}
.grid th small {
display: block;
color: var(--muted);
font-weight: 400;
}
.grid .time {
left: 0;
z-index: 1;
position: sticky;
background: var(--surface);
width: 83px;
min-width: 83px;
text-align: right;
padding: 3px 10px 3px 6px;
font-size: 12px;
color: var(--muted);
font-weight: 400;
border-right: 1px solid var(--line);
}
.grid thead .time {
z-index: 3;
top: 0;
}
.grid td {
padding: 0;
min-width: 112px;
height: 32px;
border-right: 1px solid var(--surface);
border-bottom: 1px solid var(--line);
}
.grid td.empty {
background: var(--bg);
}
.grid button.slot {
width: 100%;
height: 32px;
min-height: 32px;
padding: 0 4px;
border: 0;
border-radius: 0;
font-size: 12px;
background-color: var(--cell, var(--slot-empty));
color: var(--ink);
display: block;
position: relative;
user-select: none;
}
.grid button.slot:hover {
box-shadow: inset 0 0 0 2px var(--blue);
}
.grid button.slot.stripes {
background-image: repeating-linear-gradient(
135deg,
transparent 0 calc(var(--spacing) - 1px),
var(--stripe) calc(var(--spacing) - 1px) var(--spacing)
);
}
.grid button.slot.blocked {
background: var(--soft);
color: var(--muted);
opacity: 0.8;
}
.grid button.slot.boundary {
border-top: 2px solid var(--ink);
}
.grid .hour-mark > .time {
font-weight: 700;
color: var(--ink);
background: var(--soft);
border-top: 1px solid var(--muted);
}
.grid .hour-mark > td {
border-top: 1px solid var(--muted);
}
.grid button.slot.peer-boundary {
border-top: 2px solid var(--green);
}
.vote-mark {
position: relative;
z-index: 1;
}
.grid button.draft-slot {
background-color: var(--slot-empty);
background-image: none;
}
.draft-slot::before,
.draft-slot::after {
content: "";
position: absolute;
inset: 0;
pointer-events: none;
}
.draft-slot::before {
background-color: var(--peer-cell, transparent);
opacity: 0.75;
}
.draft-slot.peer-stripes::before {
background-image: repeating-linear-gradient(
135deg,
transparent 0 calc(var(--peer-spacing) - 1px),
var(--stripe) calc(var(--peer-spacing) - 1px) var(--peer-spacing)
);
}
.draft-slot::after {
background-color: var(--cell, transparent);
opacity: 0.35;
}
.draft-slot.stripes::after {
background-image: repeating-linear-gradient(
135deg,
transparent 0 calc(var(--spacing) - 1px),
var(--stripe) calc(var(--spacing) - 1px) var(--spacing)
);
}
.grid .gap td,
.grid .gap th {
height: 11px;
min-height: 11px;
background: var(--bg);
padding: 0;
}
.grid.editing .slot {
touch-action: none;
}
.slot svg {
width: 14px;
height: 14px;
margin: auto;
}
.grid-foot {
padding: 8px 0;
display: flex;
gap: 16px;
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
font-size: 13px;
color: var(--muted);
}
.summary-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 35px;
padding-top: 26px;
}
.summary-list {
padding: 0;
margin: 0;
list-style: none;
}
.summary-list > li {
border-top: 1px solid var(--line);
padding: 13px 0;
}
.summary-list strong {
display: block;
}
.summary-list .muted {
font-size: 13px;
}
.summary-list details {
margin-top: 6px;
font-size: 13px;
}
.summary-list details summary {
cursor: pointer;
color: var(--blue);
}
#popover {
position: fixed;
z-index: 20;
width: 290px;
max-width: calc(100vw - 24px);
max-height: 65vh;
overflow: auto;
padding: 14px;
border: 1px solid var(--line);
border-radius: 6px;
background: var(--surface);
box-shadow: var(--shadow);
}
#popover h3 {
font-size: 14px;
padding-right: 28px;
}
#popover .close {
position: absolute;
right: 5px;
top: 5px;
min-height: 28px;
width: 28px;
padding: 4px;
}
#popover .voter {
display: flex;
width: 100%;
justify-content: space-between;
text-align: left;
border: 0;
min-height: 35px;
background: transparent;
overflow-wrap: anywhere;
}
#popover .voter.absent .name {
text-decoration: line-through;
color: var(--muted);
}
#popover .voter small {
color: var(--muted);
margin-left: 10px;
}
#notice {
position: fixed;
bottom: 18px;
left: 50%;
transform: translateX(-50%);
max-width: calc(100vw - 28px);
width: max-content;
background: var(--ink);
color: var(--bg);
padding: 14px 18px;
border-radius: 6px;
box-shadow: var(--shadow);
z-index: 40;
font-size: 14px;
}
#notice button {
margin-left: 12px;
color: inherit;
background: transparent;
border: 0;
min-height: 22px;
padding: 0;
}
.busy svg {
animation: spin 1s linear infinite;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
[hidden] {
display: none !important;
}
.sr-only {
position: absolute;
width: 1px;
height: 1px;
overflow: hidden;
clip-path: inset(50%);
}
.grid.compact td,
.grid.compact button.slot {
height: 20px;
min-height: 20px;
line-height: 16px;
}
.grid.compact tbody .time {
padding-top: 0;
padding-bottom: 0;
line-height: 16px;
}
.grid.compact .gap td {
height: 11px;
min-height: 11px;
}
@media (prefers-reduced-motion: reduce) {
.busy svg {
animation: none;
}
}
@media (max-width: 700px) {
.segmented button {
font-size: 14px;
padding: 7px 8px;
gap: 5px;
}
.grid {
--day-width: 95px;
--time-width: 69px;
}
.site-header {
padding: 0 16px;
height: 58px;
}
main {
padding: 20px 14px 45px;
}
h1 {
font-size: 25px;
}
.home {
margin: 18px auto;
}
.home h1 {
font-size: 30px;
}
.home-actions {
grid-template-columns: 1fr;
gap: 28px;
}
.home-actions section + section {
padding: 24px 0 0;
border-left: 0;
border-top: 1px solid var(--line);
}
.heading {
flex-wrap: wrap;
}
.summary-grid {
grid-template-columns: 1fr;
gap: 18px;
}
.fields {
gap: 12px;
}
.fields .wide {
width: 100%;
}
.editbar {
gap: 12px;
}
.editbar .save-actions {
margin-left: 0;
}
.grid th,
.grid td {
min-width: 95px;
}
.grid .time {
min-width: 69px;
width: 69px;
padding-right: 7px;
}
.grid-scroll {
max-height: 68vh;
}
.date-entry {
gap: 6px;
}
.date-entry input {
font-size: 14px;
}
#connection {
font-size: 12px;
}
.grid button.slot,
.grid td {
height: 36px;
}
.grid button.slot {
min-height: 36px;
}
.new-person input {
width: 190px;
}
}