Add the season_calendar relation and table on the admin section
This calendar is supposed to be edited by admin users, but do not yet have the complete JavaScript code to do so, thus for now i have made it read-only.
This commit is contained in:
parent
3768dd5082
commit
ea2fe8848b
|
@ -243,5 +243,28 @@ select add_season(52, 'Temporada alta', '#ff926c');
|
|||
select add_season(52, 'Temporada mitjana', '#ffe37f');
|
||||
select add_season(52, 'Temporada baixa', '#00aa7d');
|
||||
|
||||
select set_season_range(92, '[2023-04-06, 2023-04-10]');
|
||||
select set_season_range(94, '[2023-04-11, 2023-04-27]');
|
||||
select set_season_range(93, '[2023-04-28, 2023-04-30]');
|
||||
select set_season_range(94, '[2023-05-01, 2023-06-01]');
|
||||
select set_season_range(93, '[2023-06-02, 2023-06-03]');
|
||||
select set_season_range(94, '[2023-06-04, 2023-06-08]');
|
||||
select set_season_range(93, '[2023-06-09, 2023-06-10]');
|
||||
select set_season_range(94, '[2023-06-11, 2023-06-15]');
|
||||
select set_season_range(93, '[2023-06-16, 2023-06-22]');
|
||||
select set_season_range(92, '[2023-06-23, 2023-06-25]');
|
||||
select set_season_range(93, '[2023-06-26, 2023-06-30]');
|
||||
select set_season_range(92, '[2023-07-01, 2023-08-27]');
|
||||
select set_season_range(93, '[2023-08-28, 2023-09-02]');
|
||||
select set_season_range(94, '[2023-09-03, 2023-09-07]');
|
||||
select set_season_range(93, '[2023-09-08, 2023-09-10]');
|
||||
select set_season_range(94, '[2023-09-11, 2023-09-14]');
|
||||
select set_season_range(93, '[2023-09-15, 2023-09-16]');
|
||||
select set_season_range(94, '[2023-09-17, 2023-09-21]');
|
||||
select set_season_range(93, '[2023-09-22, 2023-09-23]');
|
||||
select set_season_range(94, '[2023-09-24, 2023-09-28]');
|
||||
select set_season_range(93, '[2023-09-29, 2023-09-30]');
|
||||
select set_season_range(94, '[2023-10-01, 2023-10-12]');
|
||||
|
||||
|
||||
commit;
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
-- Deploy camper:extension_btree_gist to pg
|
||||
-- requires: schema_public
|
||||
|
||||
begin;
|
||||
|
||||
create extension if not exists btree_gist;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,55 @@
|
|||
-- Deploy camper:season_calendar to pg
|
||||
-- requires: roles
|
||||
-- requires: schema_camper
|
||||
-- requires: season
|
||||
-- requires: extension_btree_gist
|
||||
-- requires: user_profile
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to camper, public;
|
||||
|
||||
create table season_calendar (
|
||||
season_id integer not null,
|
||||
season_range daterange not null,
|
||||
primary key (season_id, season_range),
|
||||
constraint disallow_overlap exclude using gist (season_id with =, season_range with &&)
|
||||
);
|
||||
|
||||
grant select on table season_calendar to guest;
|
||||
grant select on table season_calendar to employee;
|
||||
grant select, insert, update, delete on table season_calendar to admin;
|
||||
|
||||
alter table season_calendar enable row level security;
|
||||
|
||||
create policy guest_ok
|
||||
on season_calendar
|
||||
for select
|
||||
using (true)
|
||||
;
|
||||
|
||||
create policy insert_to_company
|
||||
on season_calendar
|
||||
for insert
|
||||
with check (
|
||||
exists (select 1 from season join user_profile using (company_id) where season.season_id = season_calendar.season_id)
|
||||
)
|
||||
;
|
||||
|
||||
create policy update_company
|
||||
on season_calendar
|
||||
for update
|
||||
using (
|
||||
exists (select 1 from season join user_profile using (company_id) where season.season_id = season_calendar.season_id)
|
||||
)
|
||||
;
|
||||
|
||||
create policy delete_from_company
|
||||
on season_calendar
|
||||
for delete
|
||||
using (
|
||||
exists (select 1 from season join user_profile using (company_id) where season.season_id = season_calendar.season_id)
|
||||
)
|
||||
;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,38 @@
|
|||
-- Deploy camper:set_season_range to pg
|
||||
-- requires: roles
|
||||
-- requires: schema_camper
|
||||
-- requires: season_calendar
|
||||
-- requires: unset_season_range
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to camper, public;
|
||||
|
||||
create or replace function set_season_range(szn_id integer, range daterange) returns void as
|
||||
$$
|
||||
declare
|
||||
tmp_range daterange;
|
||||
begin
|
||||
select daterange(min(lower(season_range)), max(upper(season_range)))
|
||||
into tmp_range
|
||||
from season_calendar
|
||||
where season_id = szn_id
|
||||
and (season_range && range or season_range -|- range)
|
||||
;
|
||||
if not lower_inf(tmp_range) and not upper_inf(tmp_range) then
|
||||
range := range + tmp_range;
|
||||
end if;
|
||||
|
||||
perform unset_season_range(range);
|
||||
|
||||
insert into season_calendar (season_id, season_range)
|
||||
values (szn_id, range);
|
||||
end
|
||||
$$
|
||||
language plpgsql
|
||||
;
|
||||
|
||||
revoke execute on function set_season_range(integer, daterange) from public;
|
||||
grant execute on function set_season_range(integer, daterange) to admin;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,49 @@
|
|||
-- Deploy camper:unset_season_range to pg
|
||||
-- requires: roles
|
||||
-- requires: schema_camper
|
||||
-- requires: season_calendar
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to camper, public;
|
||||
|
||||
create or replace function unset_season_range(range daterange) returns void as
|
||||
$$
|
||||
declare
|
||||
tmp_range daterange;
|
||||
tmp_id integer;
|
||||
begin
|
||||
for tmp_id, tmp_range in
|
||||
select season_id, season_range
|
||||
from season_calendar
|
||||
where season_range @> range
|
||||
loop
|
||||
delete from season_calendar where season_id = tmp_id and season_range = tmp_range;
|
||||
insert into season_calendar (season_id, season_range)
|
||||
values (tmp_id, daterange(lower(tmp_range), lower(range)))
|
||||
, (tmp_id, daterange(upper(range), upper(tmp_range)))
|
||||
;
|
||||
end loop;
|
||||
|
||||
delete from season_calendar where range @> season_range;
|
||||
|
||||
update season_calendar
|
||||
set season_range = season_range - range
|
||||
where season_range && range
|
||||
and season_range < range
|
||||
;
|
||||
|
||||
update season_calendar
|
||||
set season_range = season_range - (season_range * range)
|
||||
where season_range && range
|
||||
and season_range > range
|
||||
;
|
||||
end
|
||||
$$
|
||||
language plpgsql
|
||||
;
|
||||
|
||||
revoke execute on function unset_season_range(daterange) from public;
|
||||
grant execute on function unset_season_range(daterange) to admin;
|
||||
|
||||
commit;
|
|
@ -8,6 +8,7 @@ package season
|
|||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"dev.tandem.ws/tandem/camper/pkg/auth"
|
||||
"dev.tandem.ws/tandem/camper/pkg/database"
|
||||
|
@ -74,12 +75,17 @@ func (h *AdminHandler) Handler(user *auth.User, company *auth.Company, conn *dat
|
|||
}
|
||||
|
||||
func serveSeasonIndex(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company, conn *database.Conn) {
|
||||
campsites, err := collectSeasonEntries(r.Context(), company, conn)
|
||||
seasons, err := collectSeasonEntries(r.Context(), company, conn)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
calendar, err := collectSeasonCalendar(r.Context(), company, conn, 2023)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
page := &seasonIndex{
|
||||
Seasons: campsites,
|
||||
Seasons: seasons,
|
||||
Calendar: calendar,
|
||||
}
|
||||
page.MustRender(w, r, user, company)
|
||||
}
|
||||
|
@ -117,8 +123,100 @@ type seasonEntry struct {
|
|||
Active bool
|
||||
}
|
||||
|
||||
var longMonthNames = []string{
|
||||
locale.PgettextNoop("January", "month"),
|
||||
locale.PgettextNoop("February", "month"),
|
||||
locale.PgettextNoop("March", "month"),
|
||||
locale.PgettextNoop("April", "month"),
|
||||
locale.PgettextNoop("May", "month"),
|
||||
locale.PgettextNoop("June", "month"),
|
||||
locale.PgettextNoop("July", "month"),
|
||||
locale.PgettextNoop("August", "month"),
|
||||
locale.PgettextNoop("September", "month"),
|
||||
locale.PgettextNoop("October", "month"),
|
||||
locale.PgettextNoop("November", "month"),
|
||||
locale.PgettextNoop("December", "month"),
|
||||
}
|
||||
|
||||
func collectSeasonCalendar(ctx context.Context, company *auth.Company, conn *database.Conn, year int) (seasonCalendar, error) {
|
||||
firstDay := time.Date(year, time.January, 1, 0, 0, 0, 0, time.UTC)
|
||||
lastDay := time.Date(year, time.December, 31, 23, 59, 59, 0, time.UTC)
|
||||
rows, err := conn.Query(ctx, `
|
||||
select t.day::date
|
||||
, to_color(coalesce(color, 13750495)) as color
|
||||
from generate_series($1, $2, interval '1 day') as t(day)
|
||||
left join season_calendar on season_range @> t.day::date
|
||||
left join season on season.season_id = season_calendar.season_id and company_id = $3
|
||||
`, firstDay, lastDay, company.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var month *seasonMonth
|
||||
var week seasonWeek
|
||||
var calendar seasonCalendar
|
||||
weekday := int(time.Monday)
|
||||
for rows.Next() {
|
||||
day := &seasonDay{}
|
||||
if err = rows.Scan(&day.Date, &day.Color); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dayMonth := day.Date.Month()
|
||||
if month == nil || month.Month != dayMonth {
|
||||
if month != nil {
|
||||
for ; weekday != int(time.Sunday); weekday = (weekday + 1) % 7 {
|
||||
week = append(week, &seasonDay{})
|
||||
}
|
||||
month.Weeks = append(month.Weeks, week)
|
||||
calendar = append(calendar, month)
|
||||
}
|
||||
month = &seasonMonth{
|
||||
Month: dayMonth,
|
||||
Name: longMonthNames[dayMonth-1],
|
||||
}
|
||||
week = seasonWeek{}
|
||||
weekday = int(time.Monday)
|
||||
dayWeekday := int(day.Date.Weekday())
|
||||
for ; weekday != dayWeekday; weekday = (weekday + 1) % 7 {
|
||||
week = append(week, &seasonDay{})
|
||||
}
|
||||
}
|
||||
week = append(week, day)
|
||||
weekday = (weekday + 1) % 7
|
||||
if weekday == int(time.Monday) {
|
||||
month.Weeks = append(month.Weeks, week)
|
||||
week = seasonWeek{}
|
||||
}
|
||||
}
|
||||
if month != nil {
|
||||
for ; weekday != int(time.Sunday); weekday = (weekday + 1) % 7 {
|
||||
week = append(week, &seasonDay{})
|
||||
}
|
||||
month.Weeks = append(month.Weeks, week)
|
||||
calendar = append(calendar, month)
|
||||
}
|
||||
|
||||
return calendar, nil
|
||||
}
|
||||
|
||||
type seasonCalendar []*seasonMonth
|
||||
|
||||
type seasonMonth struct {
|
||||
Month time.Month
|
||||
Name string
|
||||
Weeks []seasonWeek
|
||||
}
|
||||
|
||||
type seasonWeek []*seasonDay
|
||||
|
||||
type seasonDay struct {
|
||||
Date time.Time
|
||||
Color string
|
||||
}
|
||||
|
||||
type seasonIndex struct {
|
||||
Seasons []*seasonEntry
|
||||
Seasons []*seasonEntry
|
||||
Calendar seasonCalendar
|
||||
}
|
||||
|
||||
func (page *seasonIndex) MustRender(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company) {
|
||||
|
|
108
po/ca.po
108
po/ca.po
|
@ -8,7 +8,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: camper\n"
|
||||
"Report-Msgid-Bugs-To: jordi@tandem.blog\n"
|
||||
"POT-Creation-Date: 2023-09-26 17:28+0200\n"
|
||||
"POT-Creation-Date: 2023-09-27 02:15+0200\n"
|
||||
"PO-Revision-Date: 2023-07-22 23:45+0200\n"
|
||||
"Last-Translator: jordi fita mas <jordi@tandem.blog>\n"
|
||||
"Language-Team: Catalan <ca@dodds.net>\n"
|
||||
|
@ -398,6 +398,46 @@ msgstr "Color"
|
|||
msgid "No seasons added yet."
|
||||
msgstr "No s’ha afegit cap temporada encara."
|
||||
|
||||
#: web/templates/admin/season/index.gohtml:40
|
||||
msgctxt "title"
|
||||
msgid "Calendar"
|
||||
msgstr "Calendari"
|
||||
|
||||
#: web/templates/admin/season/index.gohtml:47
|
||||
msgctxt "day"
|
||||
msgid "Mon"
|
||||
msgstr "dl"
|
||||
|
||||
#: web/templates/admin/season/index.gohtml:48
|
||||
msgctxt "day"
|
||||
msgid "Tue"
|
||||
msgstr "dt"
|
||||
|
||||
#: web/templates/admin/season/index.gohtml:49
|
||||
msgctxt "day"
|
||||
msgid "Wed"
|
||||
msgstr "dc"
|
||||
|
||||
#: web/templates/admin/season/index.gohtml:50
|
||||
msgctxt "day"
|
||||
msgid "Thu"
|
||||
msgstr "dj"
|
||||
|
||||
#: web/templates/admin/season/index.gohtml:51
|
||||
msgctxt "day"
|
||||
msgid "Fri"
|
||||
msgstr "dv"
|
||||
|
||||
#: web/templates/admin/season/index.gohtml:52
|
||||
msgctxt "day"
|
||||
msgid "Sat"
|
||||
msgstr "ds"
|
||||
|
||||
#: web/templates/admin/season/index.gohtml:53
|
||||
msgctxt "day"
|
||||
msgid "Sun"
|
||||
msgstr "dg"
|
||||
|
||||
#: web/templates/admin/dashboard.gohtml:6
|
||||
#: web/templates/admin/dashboard.gohtml:10 web/templates/admin/layout.gohtml:68
|
||||
msgctxt "title"
|
||||
|
@ -761,7 +801,7 @@ msgid "Automatic"
|
|||
msgstr "Automàtic"
|
||||
|
||||
#: pkg/app/user.go:249 pkg/campsite/types/l10n.go:82
|
||||
#: pkg/campsite/types/admin.go:274 pkg/season/admin.go:203
|
||||
#: pkg/campsite/types/admin.go:274 pkg/season/admin.go:301
|
||||
#: pkg/services/l10n.go:73 pkg/services/admin.go:266
|
||||
msgid "Name can not be empty."
|
||||
msgstr "No podeu deixar el nom en blanc."
|
||||
|
@ -808,11 +848,71 @@ msgstr "El tipus d’allotjament escollit no és vàlid."
|
|||
msgid "Label can not be empty."
|
||||
msgstr "No podeu deixar l’etiqueta en blanc."
|
||||
|
||||
#: pkg/season/admin.go:204
|
||||
#: pkg/season/admin.go:127
|
||||
msgctxt "month"
|
||||
msgid "January"
|
||||
msgstr "gener"
|
||||
|
||||
#: pkg/season/admin.go:128
|
||||
msgctxt "month"
|
||||
msgid "February"
|
||||
msgstr "febrer"
|
||||
|
||||
#: pkg/season/admin.go:129
|
||||
msgctxt "month"
|
||||
msgid "March"
|
||||
msgstr "març"
|
||||
|
||||
#: pkg/season/admin.go:130
|
||||
msgctxt "month"
|
||||
msgid "April"
|
||||
msgstr "abril"
|
||||
|
||||
#: pkg/season/admin.go:131
|
||||
msgctxt "month"
|
||||
msgid "May"
|
||||
msgstr "maig"
|
||||
|
||||
#: pkg/season/admin.go:132
|
||||
msgctxt "month"
|
||||
msgid "June"
|
||||
msgstr "juny"
|
||||
|
||||
#: pkg/season/admin.go:133
|
||||
msgctxt "month"
|
||||
msgid "July"
|
||||
msgstr "juliol"
|
||||
|
||||
#: pkg/season/admin.go:134
|
||||
msgctxt "month"
|
||||
msgid "August"
|
||||
msgstr "agost"
|
||||
|
||||
#: pkg/season/admin.go:135
|
||||
msgctxt "month"
|
||||
msgid "September"
|
||||
msgstr "setembre"
|
||||
|
||||
#: pkg/season/admin.go:136
|
||||
msgctxt "month"
|
||||
msgid "October"
|
||||
msgstr "octubre"
|
||||
|
||||
#: pkg/season/admin.go:137
|
||||
msgctxt "month"
|
||||
msgid "November"
|
||||
msgstr "novembre"
|
||||
|
||||
#: pkg/season/admin.go:138
|
||||
msgctxt "month"
|
||||
msgid "December"
|
||||
msgstr "desembre"
|
||||
|
||||
#: pkg/season/admin.go:302
|
||||
msgid "Color can not be empty."
|
||||
msgstr "No podeu deixar el color en blanc."
|
||||
|
||||
#: pkg/season/admin.go:205
|
||||
#: pkg/season/admin.go:303
|
||||
msgid "This color is not valid. It must be like #123abc."
|
||||
msgstr "Aquest color no és vàlid. Hauria de ser similar a #123abc."
|
||||
|
||||
|
|
108
po/es.po
108
po/es.po
|
@ -8,7 +8,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: camper\n"
|
||||
"Report-Msgid-Bugs-To: jordi@tandem.blog\n"
|
||||
"POT-Creation-Date: 2023-09-26 17:28+0200\n"
|
||||
"POT-Creation-Date: 2023-09-27 02:15+0200\n"
|
||||
"PO-Revision-Date: 2023-07-22 23:46+0200\n"
|
||||
"Last-Translator: jordi fita mas <jordi@tandem.blog>\n"
|
||||
"Language-Team: Spanish <es@tp.org.es>\n"
|
||||
|
@ -398,6 +398,46 @@ msgstr "Color"
|
|||
msgid "No seasons added yet."
|
||||
msgstr "No se ha añadido ninguna temporada todavía."
|
||||
|
||||
#: web/templates/admin/season/index.gohtml:40
|
||||
msgctxt "title"
|
||||
msgid "Calendar"
|
||||
msgstr "Calendario"
|
||||
|
||||
#: web/templates/admin/season/index.gohtml:47
|
||||
msgctxt "day"
|
||||
msgid "Mon"
|
||||
msgstr "lu"
|
||||
|
||||
#: web/templates/admin/season/index.gohtml:48
|
||||
msgctxt "day"
|
||||
msgid "Tue"
|
||||
msgstr "ma"
|
||||
|
||||
#: web/templates/admin/season/index.gohtml:49
|
||||
msgctxt "day"
|
||||
msgid "Wed"
|
||||
msgstr "mi"
|
||||
|
||||
#: web/templates/admin/season/index.gohtml:50
|
||||
msgctxt "day"
|
||||
msgid "Thu"
|
||||
msgstr "ju"
|
||||
|
||||
#: web/templates/admin/season/index.gohtml:51
|
||||
msgctxt "day"
|
||||
msgid "Fri"
|
||||
msgstr "vi"
|
||||
|
||||
#: web/templates/admin/season/index.gohtml:52
|
||||
msgctxt "day"
|
||||
msgid "Sat"
|
||||
msgstr "sá"
|
||||
|
||||
#: web/templates/admin/season/index.gohtml:53
|
||||
msgctxt "day"
|
||||
msgid "Sun"
|
||||
msgstr "do"
|
||||
|
||||
#: web/templates/admin/dashboard.gohtml:6
|
||||
#: web/templates/admin/dashboard.gohtml:10 web/templates/admin/layout.gohtml:68
|
||||
msgctxt "title"
|
||||
|
@ -761,7 +801,7 @@ msgid "Automatic"
|
|||
msgstr "Automático"
|
||||
|
||||
#: pkg/app/user.go:249 pkg/campsite/types/l10n.go:82
|
||||
#: pkg/campsite/types/admin.go:274 pkg/season/admin.go:203
|
||||
#: pkg/campsite/types/admin.go:274 pkg/season/admin.go:301
|
||||
#: pkg/services/l10n.go:73 pkg/services/admin.go:266
|
||||
msgid "Name can not be empty."
|
||||
msgstr "No podéis dejar el nombre en blanco."
|
||||
|
@ -808,11 +848,71 @@ msgstr "El tipo de alojamiento escogido no es válido."
|
|||
msgid "Label can not be empty."
|
||||
msgstr "No podéis dejar la etiqueta en blanco."
|
||||
|
||||
#: pkg/season/admin.go:204
|
||||
#: pkg/season/admin.go:127
|
||||
msgctxt "month"
|
||||
msgid "January"
|
||||
msgstr "enero"
|
||||
|
||||
#: pkg/season/admin.go:128
|
||||
msgctxt "month"
|
||||
msgid "February"
|
||||
msgstr "febrero"
|
||||
|
||||
#: pkg/season/admin.go:129
|
||||
msgctxt "month"
|
||||
msgid "March"
|
||||
msgstr "marzo"
|
||||
|
||||
#: pkg/season/admin.go:130
|
||||
msgctxt "month"
|
||||
msgid "April"
|
||||
msgstr "abril"
|
||||
|
||||
#: pkg/season/admin.go:131
|
||||
msgctxt "month"
|
||||
msgid "May"
|
||||
msgstr "mayo"
|
||||
|
||||
#: pkg/season/admin.go:132
|
||||
msgctxt "month"
|
||||
msgid "June"
|
||||
msgstr "junio"
|
||||
|
||||
#: pkg/season/admin.go:133
|
||||
msgctxt "month"
|
||||
msgid "July"
|
||||
msgstr "julio"
|
||||
|
||||
#: pkg/season/admin.go:134
|
||||
msgctxt "month"
|
||||
msgid "August"
|
||||
msgstr "agosto"
|
||||
|
||||
#: pkg/season/admin.go:135
|
||||
msgctxt "month"
|
||||
msgid "September"
|
||||
msgstr "septiembre"
|
||||
|
||||
#: pkg/season/admin.go:136
|
||||
msgctxt "month"
|
||||
msgid "October"
|
||||
msgstr "octubre"
|
||||
|
||||
#: pkg/season/admin.go:137
|
||||
msgctxt "month"
|
||||
msgid "November"
|
||||
msgstr "noviembre"
|
||||
|
||||
#: pkg/season/admin.go:138
|
||||
msgctxt "month"
|
||||
msgid "December"
|
||||
msgstr "diciembre"
|
||||
|
||||
#: pkg/season/admin.go:302
|
||||
msgid "Color can not be empty."
|
||||
msgstr "No podéis dejar el color en blanco."
|
||||
|
||||
#: pkg/season/admin.go:205
|
||||
#: pkg/season/admin.go:303
|
||||
msgid "This color is not valid. It must be like #123abc."
|
||||
msgstr "Este color no es válido. Tiene que ser parecido a #123abc."
|
||||
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert camper:extension_btree_gist from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop extension if exists btree_gist;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert camper:season_calendar from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop table if exists camper.season_calendar;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert camper:set_season_range from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop function if exists camper.set_season_range(integer, daterange);
|
||||
|
||||
commit;
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert camper:unset_season_range from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop function if exists camper.unset_season_range(daterange);
|
||||
|
||||
commit;
|
|
@ -80,3 +80,7 @@ service_i18n [roles schema_camper service language] 2023-09-17T00:13:42Z jordi f
|
|||
translate_service [roles schema_camper service_i18n] 2023-09-17T00:17:00Z jordi fita mas <jordi@tandem.blog> # Add function to translate a service
|
||||
remove_service [roles schema_camper service service_i18n] 2023-09-26T15:21:00Z jordi fita mas <jordi@tandem.blog> # Add function to remove service
|
||||
translation [schema_camper] 2023-09-25T16:27:50Z jordi fita mas <jordi@tandem.blog> # Add the type for a translation
|
||||
extension_btree_gist [schema_public] 2023-09-26T17:49:30Z jordi fita mas <jordi@tandem.blog> # Add btree_gist extension
|
||||
season_calendar [roles schema_camper season extension_btree_gist user_profile] 2023-09-26T18:07:21Z jordi fita mas <jordi@tandem.blog> # Add the relation of date ranges for seasons
|
||||
unset_season_range [roles schema_camper season_calendar] 2023-09-26T21:56:38Z jordi fita mas <jordi@tandem.blog> # Add function to unset a date range from the seasons’ calendar
|
||||
set_season_range [roles schema_camper season_calendar unset_season_range] 2023-09-26T18:37:29Z jordi fita mas <jordi@tandem.blog> # Add function to set a season’s date range
|
||||
|
|
|
@ -8,7 +8,8 @@ begin;
|
|||
select plan(1);
|
||||
|
||||
select extensions_are(array [
|
||||
'citext'
|
||||
'btree_gist'
|
||||
, 'citext'
|
||||
, 'pgtap'
|
||||
, 'pgcrypto'
|
||||
, 'pg_libphonenumber'
|
||||
|
|
|
@ -0,0 +1,179 @@
|
|||
-- Test season_calendar
|
||||
set client_min_messages to warning;
|
||||
create extension if not exists pgtap;
|
||||
reset client_min_messages;
|
||||
|
||||
begin;
|
||||
|
||||
select plan(29);
|
||||
|
||||
set search_path to camper, public;
|
||||
|
||||
select has_table('season_calendar');
|
||||
select has_pk('season_calendar');
|
||||
select col_is_pk('season_calendar', array['season_id', 'season_range']);
|
||||
select table_privs_are('season_calendar', 'guest', array['SELECT']);
|
||||
select table_privs_are('season_calendar', 'employee', array['SELECT']);
|
||||
select table_privs_are('season_calendar', 'admin', array['SELECT', 'INSERT', 'UPDATE', 'DELETE']);
|
||||
select table_privs_are('season_calendar', 'authenticator', array[]::text[]);
|
||||
|
||||
select has_column('season_calendar', 'season_id');
|
||||
select col_type_is('season_calendar', 'season_id', 'integer');
|
||||
select col_not_null('season_calendar', 'season_id');
|
||||
select col_hasnt_default('season_calendar', 'season_id');
|
||||
|
||||
select has_column('season_calendar', 'season_range');
|
||||
select col_type_is('season_calendar', 'season_range', 'daterange');
|
||||
select col_not_null('season_calendar', 'season_range');
|
||||
select col_hasnt_default('season_calendar', 'season_range');
|
||||
|
||||
|
||||
set client_min_messages to warning;
|
||||
truncate season_calendar cascade;
|
||||
truncate season cascade;
|
||||
truncate company_host cascade;
|
||||
truncate company_user cascade;
|
||||
truncate company cascade;
|
||||
truncate auth."user" cascade;
|
||||
reset client_min_messages;
|
||||
|
||||
|
||||
insert into auth."user" (user_id, email, name, password, cookie, cookie_expires_at)
|
||||
values (1, 'demo@tandem.blog', 'Demo', 'test', '44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e', current_timestamp + interval '1 month')
|
||||
, (5, 'admin@tandem.blog', 'Demo', 'test', '12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524', current_timestamp + interval '1 month')
|
||||
;
|
||||
|
||||
insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code, currency_code, default_lang_tag)
|
||||
values (2, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', 'ES', 'EUR', 'ca')
|
||||
, (4, 'Company 4', 'XX234', '', '666-666-666', 'b@b', '', '', '', '', '', 'FR', 'USD', 'ca')
|
||||
;
|
||||
|
||||
insert into company_user (company_id, user_id, role)
|
||||
values (2, 1, 'admin')
|
||||
, (4, 5, 'admin')
|
||||
;
|
||||
|
||||
insert into company_host (company_id, host)
|
||||
values (2, 'co2')
|
||||
, (4, 'co4')
|
||||
;
|
||||
|
||||
insert into season (season_id, company_id, name)
|
||||
values (7, 2, 'Peak')
|
||||
, (8, 4, 'Low')
|
||||
;
|
||||
|
||||
insert into season_calendar (season_id, season_range)
|
||||
values (7, '[2023-01-01, 2023-02-01)')
|
||||
, (8, '[2023-02-01, 2023-03-01)')
|
||||
;
|
||||
|
||||
prepare season_data as
|
||||
select season_id, lower(season_range)::text
|
||||
from season_calendar
|
||||
;
|
||||
|
||||
set role guest;
|
||||
select bag_eq(
|
||||
'season_data',
|
||||
$$ values (7, '2023-01-01')
|
||||
, (8, '2023-02-01')
|
||||
$$,
|
||||
'Everyone should be able to list all seasons across all companies'
|
||||
);
|
||||
reset role;
|
||||
|
||||
select set_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog', 'co2');
|
||||
|
||||
select lives_ok(
|
||||
$$ insert into season_calendar(season_id, season_range) values (7, '[2023-03-01, 2023-04-01)' ) $$,
|
||||
'Admin from company 2 should be able to insert a new season to that company.'
|
||||
);
|
||||
|
||||
select bag_eq(
|
||||
'season_data',
|
||||
$$ values (7, '2023-01-01')
|
||||
, (7, '2023-03-01')
|
||||
, (8, '2023-02-01')
|
||||
$$,
|
||||
'The new row should have been added'
|
||||
);
|
||||
|
||||
select lives_ok(
|
||||
$$ update season_calendar set season_range = '[2023-04-01, 2023-05-01)' where season_id = 7 and season_range = '[2023-03-01, 2023-04-01)' $$,
|
||||
'Admin from company 2 should be able to update season of that company.'
|
||||
);
|
||||
|
||||
select bag_eq(
|
||||
'season_data',
|
||||
$$ values (7, '2023-01-01')
|
||||
, (7, '2023-04-01')
|
||||
, (8, '2023-02-01')
|
||||
$$,
|
||||
'The row should have been updated.'
|
||||
);
|
||||
|
||||
select lives_ok(
|
||||
$$ delete from season_calendar where season_id = 7 and season_range = '[2023-04-01, 2023-05-01)' $$,
|
||||
'Admin from company 2 should be able to delete season from that company.'
|
||||
);
|
||||
|
||||
select bag_eq(
|
||||
'season_data',
|
||||
$$ values (7, '2023-01-01')
|
||||
, (8, '2023-02-01')
|
||||
$$,
|
||||
'The row should have been deleted.'
|
||||
);
|
||||
|
||||
select throws_ok(
|
||||
$$ insert into season_calendar (season_id, season_range) values (8, '[2023-05-01, 2023-06-01)' ) $$,
|
||||
'42501', 'new row violates row-level security policy for table "season_calendar"',
|
||||
'Admin from company 2 should NOT be able to insert new seasons to company 4.'
|
||||
);
|
||||
|
||||
select lives_ok(
|
||||
$$ update season_calendar set season_range = '[2023-09-01, 2023-10-01)' where season_id = 8 $$,
|
||||
'Admin from company 2 should not be able to update new seasons of company 4, but no error if season_id is not changed.'
|
||||
);
|
||||
|
||||
select bag_eq(
|
||||
'season_data',
|
||||
$$ values (7, '2023-01-01')
|
||||
, (8, '2023-02-01')
|
||||
$$,
|
||||
'No row should have been changed.'
|
||||
);
|
||||
|
||||
select throws_ok(
|
||||
$$ update season_calendar set season_id = 8 where season_id = 7 $$,
|
||||
'42501', 'new row violates row-level security policy for table "season_calendar"',
|
||||
'Admin from company 2 should NOT be able to move seasons to company 4'
|
||||
);
|
||||
|
||||
select lives_ok(
|
||||
$$ delete from season_calendar where season_id = 8 $$,
|
||||
'Admin from company 2 should NOT be able to delete seasons from company 4, but not error is thrown'
|
||||
);
|
||||
|
||||
select bag_eq(
|
||||
'season_data',
|
||||
$$ values (7, '2023-01-01')
|
||||
, (8, '2023-02-01')
|
||||
$$,
|
||||
'No row should have been changed'
|
||||
);
|
||||
|
||||
select throws_ok(
|
||||
$$ insert into season_calendar (season_id, season_range) values (7, '[2023-01-30, 2023-02-02)' ) $$,
|
||||
'23P01', 'conflicting key value violates exclusion constraint "disallow_overlap"',
|
||||
'Should not be able to insert seasons with overlapping range.'
|
||||
);
|
||||
|
||||
reset role;
|
||||
|
||||
select *
|
||||
from finish();
|
||||
|
||||
rollback;
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
-- Test set_season_range
|
||||
set client_min_messages to warning;
|
||||
create extension if not exists pgtap;
|
||||
reset client_min_messages;
|
||||
|
||||
begin;
|
||||
|
||||
select plan(19);
|
||||
|
||||
set search_path to camper, public;
|
||||
|
||||
select has_function('camper', 'set_season_range', array['integer', 'daterange']);
|
||||
select function_lang_is('camper', 'set_season_range', array['integer', 'daterange'], 'plpgsql');
|
||||
select function_returns('camper', 'set_season_range', array['integer', 'daterange'], 'void');
|
||||
select isnt_definer('camper', 'set_season_range', array['integer', 'daterange']);
|
||||
select volatility_is('camper', 'set_season_range', array['integer', 'daterange'], 'volatile');
|
||||
select function_privs_are('camper', 'set_season_range', array ['integer', 'daterange'], 'guest', array[]::text[]);
|
||||
select function_privs_are('camper', 'set_season_range', array ['integer', 'daterange'], 'employee', array[]::text[]);
|
||||
select function_privs_are('camper', 'set_season_range', array ['integer', 'daterange'], 'admin', array['EXECUTE']);
|
||||
select function_privs_are('camper', 'set_season_range', array ['integer', 'daterange'], 'authenticator', array[]::text[]);
|
||||
|
||||
|
||||
set client_min_messages to warning;
|
||||
truncate season_calendar cascade;
|
||||
truncate season cascade;
|
||||
truncate company cascade;
|
||||
reset client_min_messages;
|
||||
|
||||
|
||||
insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code, currency_code, default_lang_tag)
|
||||
values (2, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', 'ES', 'EUR', 'ca')
|
||||
;
|
||||
|
||||
insert into season (season_id, company_id, name)
|
||||
values ( 7, 2, 'Peak')
|
||||
, ( 8, 2, 'Shoulder')
|
||||
, ( 9, 2, 'Off-Season')
|
||||
, (10, 2, 'Saint John’s Eve')
|
||||
;
|
||||
|
||||
insert into season_calendar (season_id, season_range)
|
||||
values (7, '[2023-04-07, 2023-04-09]')
|
||||
, (9, '[2023-04-12, 2023-04-16]')
|
||||
, (8, '[2023-04-18, 2023-04-22]')
|
||||
, (9, '[2023-04-24, 2023-05-14]')
|
||||
, (9, '[2023-05-22, 2023-05-28]')
|
||||
, (9, '[2023-06-05, 2023-06-15]')
|
||||
, (8, '[2023-06-16, 2023-06-30]')
|
||||
;
|
||||
|
||||
select lives_ok($$ select set_season_range( 7, '[2023-04-06, 2023-04-07]') $$);
|
||||
select lives_ok($$ select set_season_range( 7, '[2023-04-08, 2023-04-10]') $$);
|
||||
select lives_ok($$ select set_season_range( 9, '[2023-04-11, 2023-06-15]') $$);
|
||||
select lives_ok($$ select set_season_range( 8, '[2023-04-28, 2023-04-30]') $$);
|
||||
select lives_ok($$ select set_season_range( 8, '[2023-06-02, 2023-06-03]') $$);
|
||||
select lives_ok($$ select set_season_range( 8, '[2023-06-09, 2023-06-10]') $$);
|
||||
select lives_ok($$ select set_season_range(10, '[2023-06-23, 2023-06-25]') $$);
|
||||
select lives_ok($$ select set_season_range( 7, '[2023-07-01, 2023-08-27]') $$);
|
||||
select lives_ok($$ select set_season_range( 8, '[2023-08-28, 2023-08-31]') $$);
|
||||
|
||||
select bag_eq(
|
||||
$$ select season_id, season_range from season_calendar $$,
|
||||
$$ values ( 7, '[2023-04-06, 2023-04-10]'::daterange)
|
||||
, ( 9, '[2023-04-11, 2023-04-27]'::daterange)
|
||||
, ( 8, '[2023-04-28, 2023-04-30]'::daterange)
|
||||
, ( 9, '[2023-05-01, 2023-06-01]'::daterange)
|
||||
, ( 8, '[2023-06-02, 2023-06-03]'::daterange)
|
||||
, ( 9, '[2023-06-04, 2023-06-08]'::daterange)
|
||||
, ( 8, '[2023-06-09, 2023-06-10]'::daterange)
|
||||
, ( 9, '[2023-06-11, 2023-06-15]'::daterange)
|
||||
, ( 8, '[2023-06-16, 2023-06-22]'::daterange)
|
||||
, (10, '[2023-06-23, 2023-06-25]'::daterange)
|
||||
, ( 8, '[2023-06-26, 2023-06-30]'::daterange)
|
||||
, ( 7, '[2023-07-01, 2023-08-27]'::daterange)
|
||||
, ( 8, '[2023-08-28, 2023-08-31]'::daterange)
|
||||
$$,
|
||||
'Should have updated the calendar'
|
||||
);
|
||||
|
||||
select *
|
||||
from finish();
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,75 @@
|
|||
-- Test unset_season_range
|
||||
set client_min_messages to warning;
|
||||
create extension if not exists pgtap;
|
||||
reset client_min_messages;
|
||||
|
||||
begin;
|
||||
|
||||
select plan(14);
|
||||
|
||||
set search_path to camper, public;
|
||||
|
||||
select has_function('camper', 'unset_season_range', array['daterange']);
|
||||
select function_lang_is('camper', 'unset_season_range', array['daterange'], 'plpgsql');
|
||||
select function_returns('camper', 'unset_season_range', array['daterange'], 'void');
|
||||
select isnt_definer('camper', 'unset_season_range', array['daterange']);
|
||||
select volatility_is('camper', 'unset_season_range', array['daterange'], 'volatile');
|
||||
select function_privs_are('camper', 'unset_season_range', array ['daterange'], 'guest', array[]::text[]);
|
||||
select function_privs_are('camper', 'unset_season_range', array ['daterange'], 'employee', array[]::text[]);
|
||||
select function_privs_are('camper', 'unset_season_range', array ['daterange'], 'admin', array['EXECUTE']);
|
||||
select function_privs_are('camper', 'unset_season_range', array ['daterange'], 'authenticator', array[]::text[]);
|
||||
|
||||
|
||||
set client_min_messages to warning;
|
||||
truncate season_calendar cascade;
|
||||
truncate season cascade;
|
||||
truncate company cascade;
|
||||
reset client_min_messages;
|
||||
|
||||
|
||||
insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code, currency_code, default_lang_tag)
|
||||
values (2, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', 'ES', 'EUR', 'ca')
|
||||
;
|
||||
|
||||
insert into season (season_id, company_id, name)
|
||||
values ( 7, 2, 'Peak')
|
||||
, ( 8, 2, 'Shoulder')
|
||||
, ( 9, 2, 'Off-Season')
|
||||
, (10, 2, 'Saint John’s Eve')
|
||||
;
|
||||
|
||||
insert into season_calendar (season_id, season_range)
|
||||
values ( 9, '[2023-04-01, 2023-04-05]')
|
||||
, ( 7, '[2023-04-06, 2023-04-10]')
|
||||
, ( 9, '[2023-04-11, 2023-04-12]')
|
||||
, ( 8, '[2023-04-13, 2023-04-16]')
|
||||
, ( 9, '[2023-04-17, 2023-04-19]')
|
||||
, (10, '[2023-04-20, 2023-04-22]')
|
||||
, ( 9, '[2023-04-23, 2023-04-27]')
|
||||
, ( 8, '[2023-04-28, 2023-04-30]')
|
||||
;
|
||||
|
||||
select lives_ok($$ select unset_season_range('[2023-04-08, 2023-04-11]') $$);
|
||||
select lives_ok($$ select unset_season_range('[2023-04-15, 2023-04-15]') $$);
|
||||
select lives_ok($$ select unset_season_range('[2023-04-20, 2023-04-23]') $$);
|
||||
select lives_ok($$ select unset_season_range('[2023-04-26, 2023-04-29]') $$);
|
||||
|
||||
select bag_eq(
|
||||
$$ select season_id, season_range from season_calendar $$,
|
||||
$$ values ( 9, '[2023-04-01, 2023-04-05]'::daterange)
|
||||
, ( 7, '[2023-04-06, 2023-04-07]'::daterange)
|
||||
, ( 9, '[2023-04-12, 2023-04-12]'::daterange)
|
||||
, ( 8, '[2023-04-13, 2023-04-14]'::daterange)
|
||||
, ( 8, '[2023-04-16, 2023-04-16]'::daterange)
|
||||
, ( 9, '[2023-04-17, 2023-04-19]'::daterange)
|
||||
, ( 9, '[2023-04-24, 2023-04-25]'::daterange)
|
||||
, ( 8, '[2023-04-30, 2023-04-30]'::daterange)
|
||||
$$,
|
||||
'Should have updated the calendar'
|
||||
);
|
||||
|
||||
|
||||
select *
|
||||
from finish();
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,10 @@
|
|||
-- Verify camper:extension_btree_gist on pg
|
||||
|
||||
begin;
|
||||
|
||||
select 1 / count(*)
|
||||
from pg_extension
|
||||
where extname = 'btree_gist'
|
||||
;
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,16 @@
|
|||
-- Verify camper:season_calendar on pg
|
||||
|
||||
begin;
|
||||
|
||||
select season_id
|
||||
, season_range
|
||||
from camper.season_calendar
|
||||
where false;
|
||||
|
||||
select 1 / count(*) from pg_class where oid = 'camper.season_calendar'::regclass and relrowsecurity;
|
||||
select 1 / count(*) from pg_policy where polname = 'guest_ok' and polrelid = 'camper.season_calendar'::regclass;
|
||||
select 1 / count(*) from pg_policy where polname = 'insert_to_company' and polrelid = 'camper.season_calendar'::regclass;
|
||||
select 1 / count(*) from pg_policy where polname = 'update_company' and polrelid = 'camper.season_calendar'::regclass;
|
||||
select 1 / count(*) from pg_policy where polname = 'delete_from_company' and polrelid = 'camper.season_calendar'::regclass;
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,7 @@
|
|||
-- Verify camper:set_season_range on pg
|
||||
|
||||
begin;
|
||||
|
||||
select has_function_privilege('camper.set_season_range(integer, daterange)', 'execute');
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,7 @@
|
|||
-- Verify camper:unset_season_range on pg
|
||||
|
||||
begin;
|
||||
|
||||
select has_function_privilege('camper.unset_season_range(daterange)', 'execute');
|
||||
|
||||
rollback;
|
|
@ -123,3 +123,14 @@ a.missing-translation {
|
|||
.icon-input button[aria-pressed="true"] {
|
||||
background-color: #ffeeaa;
|
||||
}
|
||||
|
||||
.season-calendar {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, auto);
|
||||
justify-content: center;
|
||||
gap: 2em;
|
||||
}
|
||||
|
||||
.season-calendar svg {
|
||||
max-width: 5rem;
|
||||
}
|
||||
|
|
|
@ -36,4 +36,39 @@
|
|||
{{ else -}}
|
||||
<p>{{( gettext "No seasons added yet." )}}</p>
|
||||
{{- end }}
|
||||
|
||||
<h2>{{( pgettext "Calendar" "title" )}}</h2>
|
||||
<div class="season-calendar">
|
||||
{{ range .Calendar -}}
|
||||
<table>
|
||||
<caption>{{ pgettext .Name "month" }}</caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">{{(pgettext "Mon" "day" )}}</th>
|
||||
<th scope="col">{{(pgettext "Tue" "day" )}}</th>
|
||||
<th scope="col">{{(pgettext "Wed" "day" )}}</th>
|
||||
<th scope="col">{{(pgettext "Thu" "day" )}}</th>
|
||||
<th scope="col">{{(pgettext "Fri" "day" )}}</th>
|
||||
<th scope="col">{{(pgettext "Sat" "day" )}}</th>
|
||||
<th scope="col">{{(pgettext "Sun" "day" )}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{ range .Weeks }}
|
||||
<tr>
|
||||
{{ range . }}
|
||||
<td>
|
||||
{{- if .Color -}}
|
||||
<svg viewBox="0 0 100 100">
|
||||
<rect x="0" y="0" width="100" height="100" fill="{{ .Color }}"/>
|
||||
</svg>
|
||||
{{- end -}}
|
||||
</td>
|
||||
{{- end }}
|
||||
</tr>
|
||||
{{- end }}
|
||||
</tbody>
|
||||
</table>
|
||||
{{- end }}
|
||||
</div>
|
||||
{{- end }}
|
||||
|
|
Loading…
Reference in New Issue