Bring back the whole list of options in type page, but in accordion
This is how the customer wants it.
This commit is contained in:
parent
12d5356455
commit
4d0123def7
|
@ -7,6 +7,7 @@ package types
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"github.com/jackc/pgx/v4"
|
||||||
gotemplate "html/template"
|
gotemplate "html/template"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
@ -78,7 +79,12 @@ type typePrice struct {
|
||||||
SeasonColor string
|
SeasonColor string
|
||||||
MinNights int
|
MinNights int
|
||||||
PricePerNight string
|
PricePerNight string
|
||||||
HasOptions bool
|
Options []*optionPrice
|
||||||
|
}
|
||||||
|
|
||||||
|
type optionPrice struct {
|
||||||
|
OptionName string
|
||||||
|
PricePerNight string
|
||||||
}
|
}
|
||||||
|
|
||||||
type typeFeature struct {
|
type typeFeature struct {
|
||||||
|
@ -131,33 +137,29 @@ func collectPrices(ctx context.Context, conn *database.Conn, language language.T
|
||||||
select coalesce(i18n.name, season.name) as l10n_name
|
select coalesce(i18n.name, season.name) as l10n_name
|
||||||
, to_color(season.color)::text
|
, to_color(season.color)::text
|
||||||
, coalesce(cost.min_nights, 1)
|
, coalesce(cost.min_nights, 1)
|
||||||
, to_price(coalesce(cost.cost_per_night, 0) + coalesce(option.cost_per_night, 0))
|
, to_price(coalesce(cost.cost_per_night, 0))
|
||||||
, option.cost_per_night is not null
|
, array_agg((coalesce(option_i18n.name, option.name), to_price(coalesce(option_cost.cost_per_night, 0))) order by option.position) filter (where option.campsite_type_option_id is not null)
|
||||||
, season.position
|
|
||||||
from season
|
from season
|
||||||
left join season_i18n as i18n on season.season_id = i18n.season_id and i18n.lang_tag = $1
|
left join season_i18n as i18n on season.season_id = i18n.season_id and i18n.lang_tag = $1
|
||||||
left join (
|
left join (
|
||||||
campsite_type_cost as cost join campsite_type as type on cost.campsite_type_id = type.campsite_type_id and type.slug = $2
|
campsite_type_cost as cost join campsite_type as type on cost.campsite_type_id = type.campsite_type_id and type.slug = $2
|
||||||
) as cost on cost.season_id = season.season_id
|
) as cost on cost.season_id = season.season_id
|
||||||
left join (
|
left join (
|
||||||
select season_id
|
select option.*
|
||||||
, sum(lower(range) * cost_per_night)::integer as cost_per_night
|
from campsite_type_option as option
|
||||||
from campsite_type_option
|
join campsite_type as type on option.campsite_type_id = type.campsite_type_id and type.slug = $2
|
||||||
join campsite_type using(campsite_type_id)
|
) as option on true
|
||||||
join campsite_type_option_cost using (campsite_type_option_id)
|
left join campsite_type_option_i18n as option_i18n on option_i18n.campsite_type_option_id = option.campsite_type_option_id and option_i18n.lang_tag = $1
|
||||||
where slug = $2
|
left join campsite_type_option_cost as option_cost on option_cost.campsite_type_option_id = option.campsite_type_option_id and option_cost.season_id = season.season_id
|
||||||
group by season_id
|
|
||||||
) as option on option.season_id = season.season_id
|
|
||||||
where season.active
|
where season.active
|
||||||
union all
|
group by i18n.name
|
||||||
select $3
|
, season.name
|
||||||
, to_color($4)::text
|
, season.color
|
||||||
, 1
|
, cost.min_nights
|
||||||
, ''
|
, cost.cost_per_night
|
||||||
, false
|
, season.position
|
||||||
, 2147483647 as position
|
order by season.position, l10n_name
|
||||||
order by position, l10n_name
|
`, pgx.QueryResultFormats{pgx.BinaryFormatCode}, language, slug)
|
||||||
`, language, slug, locale.PgettextNoop("Closed", "season"), season.UnsetColor)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -165,10 +167,16 @@ func collectPrices(ctx context.Context, conn *database.Conn, language language.T
|
||||||
var prices []*typePrice
|
var prices []*typePrice
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
price := &typePrice{}
|
price := &typePrice{}
|
||||||
var position int
|
var options database.RecordArray
|
||||||
if err := rows.Scan(&price.SeasonName, &price.SeasonColor, &price.MinNights, &price.PricePerNight, &price.HasOptions, &position); err != nil {
|
if err := rows.Scan(&price.SeasonName, &price.SeasonColor, &price.MinNights, &price.PricePerNight, &options); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
for _, el := range options.Elements {
|
||||||
|
price.Options = append(price.Options, &optionPrice{
|
||||||
|
OptionName: el.Fields[0].Get().(string),
|
||||||
|
PricePerNight: el.Fields[1].Get().(string),
|
||||||
|
})
|
||||||
|
}
|
||||||
prices = append(prices, price)
|
prices = append(prices, price)
|
||||||
}
|
}
|
||||||
return prices, nil
|
return prices, nil
|
||||||
|
|
|
@ -467,12 +467,12 @@ nav:last-of-type > ul > li:last-child {
|
||||||
padding: 1.5rem 2rem;
|
padding: 1.5rem 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nature div:first-child a span, .services a span, .surroundings .spiel a:hover span, .campsite_type_booking form button span {
|
.nature div:first-child a span, .services a span, .surroundings .spiel a:hover span, .campsite_type_booking button span {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
transition: transform 0.5s ease;
|
transition: transform 0.5s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nature div:first-child a:hover span, .services a:hover span, .spiel a:hover span, .campsite_type_booking form button:hover span {
|
.nature div:first-child a:hover span, .services a:hover span, .spiel a:hover span, .campsite_type_booking button:hover span {
|
||||||
transform: translateX(1.3rem);
|
transform: translateX(1.3rem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -785,37 +785,39 @@ dt {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.campsite_type_booking form {
|
.campsite_type_booking fieldset, .campsite_type_booking footer {
|
||||||
flex: .4;
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.campsite_type_booking {
|
||||||
background-color: var(--accent);
|
background-color: var(--accent);
|
||||||
padding: 2rem;
|
padding: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.campsite_type_booking form fieldset {
|
.campsite_type_booking fieldset {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 2.5rem;
|
gap: 2.5rem;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.campsite_type_booking form label {
|
.campsite_type_booking label {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
font-size: 2rem;
|
font-size: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.campsite_type_booking form input {
|
.campsite_type_booking input {
|
||||||
padding: 1.5rem .5rem;
|
padding: 1.5rem .5rem;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
background-color: var(--base);
|
background-color: var(--base);
|
||||||
}
|
}
|
||||||
|
|
||||||
.campsite_type_booking form footer {
|
.campsite_type_booking footer {
|
||||||
margin-top: 2rem;
|
margin-top: 2rem;
|
||||||
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
.campsite_type_booking form button {
|
.campsite_type_booking button {
|
||||||
width: 100%;
|
|
||||||
text-align: left;
|
|
||||||
background-color: var(--clar);
|
background-color: var(--clar);
|
||||||
padding: 1.5rem 2rem;
|
padding: 1.5rem 2rem;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
@ -824,24 +826,35 @@ dt {
|
||||||
line-height: 0.9em;
|
line-height: 0.9em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.campsite_type_booking form,
|
.campsite_type_booking,
|
||||||
.campsite_type_booking form button,
|
.campsite_type_booking button,
|
||||||
.campsite_type_booking form input {
|
.campsite_type_booking input {
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.campsite_type_calendar_prices {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row-reverse;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 48rem) {
|
||||||
|
.campsite_type_calendar_prices {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.campsite_type_prices {
|
.campsite_type_prices {
|
||||||
flex: .6;
|
|
||||||
padding: 2.5rem;
|
padding: 2.5rem;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
border: 3px solid black;
|
flex: .5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.campsite_type_prices dl {
|
.campsite_type_prices dl {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
flex-direction: column;
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
|
border-bottom: 1px solid black;
|
||||||
}
|
}
|
||||||
|
|
||||||
.campsite_type_prices dl div:hover {
|
.campsite_type_prices dl div:hover {
|
||||||
|
@ -850,18 +863,49 @@ dt {
|
||||||
|
|
||||||
.campsite_type_prices div {
|
.campsite_type_prices div {
|
||||||
flex-basis: unset;
|
flex-basis: unset;
|
||||||
|
min-height: 0;
|
||||||
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.campsite_type_prices dt {
|
.campsite_type_prices dt {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: .5rem;
|
gap: 1.5rem;
|
||||||
border: none;
|
border-top: 1px solid black;
|
||||||
padding: 0;
|
border-bottom: none;
|
||||||
|
padding: .5em 0 0;
|
||||||
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.campsite_type_prices dt::after {
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
top: 50%;
|
||||||
|
content: '+';
|
||||||
|
width: 1em;
|
||||||
|
aspect-ratio: 1;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
background-color: black;
|
||||||
|
color: white;
|
||||||
|
border-radius: 50%;
|
||||||
|
line-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.campsite_type_prices dt.open::after {
|
||||||
|
content: '-';
|
||||||
|
}
|
||||||
|
|
||||||
|
.campsite_type_prices dd {
|
||||||
|
padding: 0 0 0 calc(30px + 1.5rem);
|
||||||
}
|
}
|
||||||
|
|
||||||
.campsite_type_calendar {
|
.campsite_type_calendar {
|
||||||
padding: 5rem 0 2.5rem;
|
padding: 2.5rem 0;
|
||||||
|
min-width: 0;
|
||||||
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.campsite_type_features li {
|
.campsite_type_features li {
|
||||||
|
@ -936,6 +980,7 @@ dt {
|
||||||
.campsite_type_calendar button {
|
.campsite_type_calendar button {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 1em;
|
gap: 1em;
|
||||||
|
font-size: 2.5rem;
|
||||||
border: none;
|
border: none;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
<link rel="stylesheet" media="screen" href="/static/camper.css">
|
<link rel="stylesheet" media="screen" href="/static/camper.css">
|
||||||
<link rel="stylesheet" media="screen" href="/static/icons.css">
|
<link rel="stylesheet" media="screen" href="/static/icons.css">
|
||||||
<script src="/static/sortable@1.15.1.min.js"></script>
|
<script src="/static/sortable@1.15.1.min.js"></script>
|
||||||
<script src="/static/alpinejs@3.13.3.min.js"></script>
|
<script src="/static/alpinejs@3.13.3.min.js" defer></script>
|
||||||
<script src="/static/htmx@1.9.3.min.js"></script>
|
<script src="/static/htmx@1.9.3.min.js"></script>
|
||||||
<script type="module" src="/static/camper.js"></script>
|
<script type="module" src="/static/camper.js"></script>
|
||||||
{{ block "head" . }}{{ end }}
|
{{ block "head" . }}{{ end }}
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
{{ define "head" -}}
|
{{ define "head" -}}
|
||||||
{{ template "carouselStyle" }}
|
{{ template "carouselStyle" }}
|
||||||
|
<script src="/static/alpinejs@3.13.3.min.js" defer></script>
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|
||||||
{{ define "content" -}}
|
{{ define "content" -}}
|
||||||
|
@ -33,48 +34,58 @@
|
||||||
</div>
|
</div>
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|
||||||
<div class="campsite_type_booking">
|
<form action="/{{ currentLocale }}/booking" method="get" class="campsite_type_booking">
|
||||||
<form action="/{{ currentLocale }}/booking" method="get">
|
<input type="hidden" name="campsite_type" value="{{ .Slug }}">
|
||||||
<input type="hidden" name="campsite_type" value="{{ .Slug }}">
|
<fieldset>
|
||||||
<fieldset>
|
<label>
|
||||||
<label>
|
{{( pgettext "Check-in Date" "input")}}
|
||||||
{{( pgettext "Check-in Date" "input")}}
|
<br>
|
||||||
<br>
|
<input name="arrival_date" type="date" required>
|
||||||
<input name="arrival_date" type="date" required>
|
<br>
|
||||||
<br>
|
</label>
|
||||||
</label>
|
<label>
|
||||||
<label>
|
{{( pgettext "Check-out Date" "input")}}
|
||||||
{{( pgettext "Check-out Date" "input")}}
|
<br>
|
||||||
<br>
|
<input name="departure_date" type="date" required>
|
||||||
<input name="departure_date" type="date" required>
|
<br>
|
||||||
<br>
|
</label>
|
||||||
</label>
|
</fieldset>
|
||||||
</fieldset>
|
<footer>
|
||||||
<footer>
|
<button type="submit">{{( pgettext "Book" "action" )}} <span>→</span></button>
|
||||||
<button type="submit">{{( pgettext "Book" "action" )}} <span>→</span></button>
|
</footer>
|
||||||
</footer>
|
</form>
|
||||||
</form>
|
|
||||||
|
<div class="campsite_type_calendar_prices">
|
||||||
|
<article class="campsite_type_calendar">
|
||||||
|
{{- /*gotype: dev.tandem.ws/tandem/camper/pkg/season.Calendar*/ -}}
|
||||||
|
<header>
|
||||||
|
<h3 class="sr-only">{{( pgettext "Calendar" "title" )}}</h3>
|
||||||
|
<button type="button"><span class="sr-only">{{ pgettext "Prev" "navigation" }}</span></button>
|
||||||
|
<button type="button"><span class="sr-only">{{ pgettext "Next" "navigation" }}</span></button>
|
||||||
|
</header>
|
||||||
|
<div>
|
||||||
|
{{ template "calendar.gohtml" .Calendar }}
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
|
||||||
{{ with .Prices -}}
|
{{ with .Prices -}}
|
||||||
<article class="campsite_type_prices">
|
<article class="campsite_type_prices">
|
||||||
<h3 class="sr-only">{{( pgettext "Prices" "title" )}}</h3>
|
<h3 class="sr-only">{{( pgettext "Prices" "title" )}}</h3>
|
||||||
<dl>
|
<dl>
|
||||||
{{ range . -}}
|
{{ range . -}}
|
||||||
<div>
|
<div x-data="{open: false}">
|
||||||
<dt>
|
<dt @click="open = !open" :class="open && 'open'">
|
||||||
<svg width="20px" height="20px">
|
<svg width="30px" height="30px">
|
||||||
<circle cx="50%" cy="50%" r="49%" fill="{{ .SeasonColor }}" stroke="#000"
|
<circle cx="50%" cy="50%" r="49%" fill="{{ .SeasonColor }}"/>
|
||||||
stroke-width=".5"/>
|
|
||||||
</svg>
|
</svg>
|
||||||
{{ .SeasonName }}
|
{{ .SeasonName }}
|
||||||
</dt>
|
</dt>
|
||||||
{{ if .HasOptions -}}
|
<dd x-show="open">{{ printf (gettext "%s €/night") .PricePerNight }}</dd>
|
||||||
<dd>{{ printf (gettext "Starting from %s €/night") .PricePerNight }}</dd>
|
{{ range .Options }}
|
||||||
{{- else if .PricePerNight -}}
|
<dd x-show="open">{{ printf (gettext "%s: %s €/night") .OptionName .PricePerNight }}</dd>
|
||||||
<dd>{{ printf (gettext "%s €/night") .PricePerNight }}</dd>
|
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{ if gt .MinNights 1 -}}
|
{{ if gt .MinNights 1 -}}
|
||||||
<dd>{{ printf (gettext "*Minimum %d nights per stay") .MinNights }}</dd>
|
<dd x-show="open">{{ printf (gettext "*Minimum %d nights per stay") .MinNights }}</dd>
|
||||||
{{- end }}
|
{{- end }}
|
||||||
</div>
|
</div>
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
@ -83,18 +94,6 @@
|
||||||
{{- end }}
|
{{- end }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<article class="campsite_type_calendar">
|
|
||||||
{{- /*gotype: dev.tandem.ws/tandem/camper/pkg/season.Calendar*/ -}}
|
|
||||||
<header>
|
|
||||||
<h3 class="sr-only">{{( pgettext "Calendar" "title" )}}</h3>
|
|
||||||
<button type="button"><span class="sr-only">{{ pgettext "Prev" "navigation" }}</span></button>
|
|
||||||
<button type="button"><span class="sr-only">{{ pgettext "Next" "navigation" }}</span></button>
|
|
||||||
</header>
|
|
||||||
<div>
|
|
||||||
{{ template "calendar.gohtml" .Calendar }}
|
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
|
|
||||||
{{ with .Features -}}
|
{{ with .Features -}}
|
||||||
<article class="campsite_type_features">
|
<article class="campsite_type_features">
|
||||||
<h3 class="sr-only">{{( pgettext "Features" "title" )}}</h3>
|
<h3 class="sr-only">{{( pgettext "Features" "title" )}}</h3>
|
||||||
|
|
Loading…
Reference in New Issue