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 (
|
||||
"context"
|
||||
"github.com/jackc/pgx/v4"
|
||||
gotemplate "html/template"
|
||||
"net/http"
|
||||
"time"
|
||||
|
@ -78,7 +79,12 @@ type typePrice struct {
|
|||
SeasonColor string
|
||||
MinNights int
|
||||
PricePerNight string
|
||||
HasOptions bool
|
||||
Options []*optionPrice
|
||||
}
|
||||
|
||||
type optionPrice struct {
|
||||
OptionName string
|
||||
PricePerNight string
|
||||
}
|
||||
|
||||
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
|
||||
, to_color(season.color)::text
|
||||
, coalesce(cost.min_nights, 1)
|
||||
, to_price(coalesce(cost.cost_per_night, 0) + coalesce(option.cost_per_night, 0))
|
||||
, option.cost_per_night is not null
|
||||
, season.position
|
||||
, to_price(coalesce(cost.cost_per_night, 0))
|
||||
, 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)
|
||||
from season
|
||||
left join season_i18n as i18n on season.season_id = i18n.season_id and i18n.lang_tag = $1
|
||||
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
|
||||
) as cost on cost.season_id = season.season_id
|
||||
left join (
|
||||
select season_id
|
||||
, sum(lower(range) * cost_per_night)::integer as cost_per_night
|
||||
from campsite_type_option
|
||||
join campsite_type using(campsite_type_id)
|
||||
join campsite_type_option_cost using (campsite_type_option_id)
|
||||
where slug = $2
|
||||
group by season_id
|
||||
) as option on option.season_id = season.season_id
|
||||
select option.*
|
||||
from campsite_type_option as option
|
||||
join campsite_type as type on option.campsite_type_id = type.campsite_type_id and type.slug = $2
|
||||
) as option on true
|
||||
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
|
||||
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
|
||||
where season.active
|
||||
union all
|
||||
select $3
|
||||
, to_color($4)::text
|
||||
, 1
|
||||
, ''
|
||||
, false
|
||||
, 2147483647 as position
|
||||
order by position, l10n_name
|
||||
`, language, slug, locale.PgettextNoop("Closed", "season"), season.UnsetColor)
|
||||
group by i18n.name
|
||||
, season.name
|
||||
, season.color
|
||||
, cost.min_nights
|
||||
, cost.cost_per_night
|
||||
, season.position
|
||||
order by season.position, l10n_name
|
||||
`, pgx.QueryResultFormats{pgx.BinaryFormatCode}, language, slug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -165,10 +167,16 @@ func collectPrices(ctx context.Context, conn *database.Conn, language language.T
|
|||
var prices []*typePrice
|
||||
for rows.Next() {
|
||||
price := &typePrice{}
|
||||
var position int
|
||||
if err := rows.Scan(&price.SeasonName, &price.SeasonColor, &price.MinNights, &price.PricePerNight, &price.HasOptions, &position); err != nil {
|
||||
var options database.RecordArray
|
||||
if err := rows.Scan(&price.SeasonName, &price.SeasonColor, &price.MinNights, &price.PricePerNight, &options); err != nil {
|
||||
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)
|
||||
}
|
||||
return prices, nil
|
||||
|
|
|
@ -467,12 +467,12 @@ nav:last-of-type > ul > li:last-child {
|
|||
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;
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -785,37 +785,39 @@ dt {
|
|||
}
|
||||
}
|
||||
|
||||
.campsite_type_booking form {
|
||||
flex: .4;
|
||||
.campsite_type_booking fieldset, .campsite_type_booking footer {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.campsite_type_booking {
|
||||
background-color: var(--accent);
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.campsite_type_booking form fieldset {
|
||||
.campsite_type_booking fieldset {
|
||||
display: flex;
|
||||
gap: 2.5rem;
|
||||
padding: 0;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.campsite_type_booking form label {
|
||||
.campsite_type_booking label {
|
||||
flex: 1;
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.campsite_type_booking form input {
|
||||
.campsite_type_booking input {
|
||||
padding: 1.5rem .5rem;
|
||||
width: 100%;
|
||||
background-color: var(--base);
|
||||
}
|
||||
|
||||
.campsite_type_booking form footer {
|
||||
.campsite_type_booking footer {
|
||||
margin-top: 2rem;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.campsite_type_booking form button {
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
.campsite_type_booking button {
|
||||
background-color: var(--clar);
|
||||
padding: 1.5rem 2rem;
|
||||
cursor: pointer;
|
||||
|
@ -824,24 +826,35 @@ dt {
|
|||
line-height: 0.9em;
|
||||
}
|
||||
|
||||
.campsite_type_booking form,
|
||||
.campsite_type_booking form button,
|
||||
.campsite_type_booking form input {
|
||||
.campsite_type_booking,
|
||||
.campsite_type_booking button,
|
||||
.campsite_type_booking input {
|
||||
border: none;
|
||||
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 {
|
||||
flex: .6;
|
||||
padding: 2.5rem;
|
||||
border-radius: 5px;
|
||||
border: 3px solid black;
|
||||
flex: .5;
|
||||
}
|
||||
|
||||
.campsite_type_prices dl {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
border-bottom: 1px solid black;
|
||||
}
|
||||
|
||||
.campsite_type_prices dl div:hover {
|
||||
|
@ -850,18 +863,49 @@ dt {
|
|||
|
||||
.campsite_type_prices div {
|
||||
flex-basis: unset;
|
||||
min-height: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.campsite_type_prices dt {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: .5rem;
|
||||
border: none;
|
||||
padding: 0;
|
||||
gap: 1.5rem;
|
||||
border-top: 1px solid black;
|
||||
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 {
|
||||
padding: 5rem 0 2.5rem;
|
||||
padding: 2.5rem 0;
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.campsite_type_features li {
|
||||
|
@ -936,6 +980,7 @@ dt {
|
|||
.campsite_type_calendar button {
|
||||
display: flex;
|
||||
gap: 1em;
|
||||
font-size: 2.5rem;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
<link rel="stylesheet" media="screen" href="/static/camper.css">
|
||||
<link rel="stylesheet" media="screen" href="/static/icons.css">
|
||||
<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 type="module" src="/static/camper.js"></script>
|
||||
{{ block "head" . }}{{ end }}
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
{{ define "head" -}}
|
||||
{{ template "carouselStyle" }}
|
||||
<script src="/static/alpinejs@3.13.3.min.js" defer></script>
|
||||
{{- end }}
|
||||
|
||||
{{ define "content" -}}
|
||||
|
@ -33,48 +34,58 @@
|
|||
</div>
|
||||
{{- end }}
|
||||
|
||||
<div class="campsite_type_booking">
|
||||
<form action="/{{ currentLocale }}/booking" method="get">
|
||||
<input type="hidden" name="campsite_type" value="{{ .Slug }}">
|
||||
<fieldset>
|
||||
<label>
|
||||
{{( pgettext "Check-in Date" "input")}}
|
||||
<br>
|
||||
<input name="arrival_date" type="date" required>
|
||||
<br>
|
||||
</label>
|
||||
<label>
|
||||
{{( pgettext "Check-out Date" "input")}}
|
||||
<br>
|
||||
<input name="departure_date" type="date" required>
|
||||
<br>
|
||||
</label>
|
||||
</fieldset>
|
||||
<footer>
|
||||
<button type="submit">{{( pgettext "Book" "action" )}} <span>→</span></button>
|
||||
</footer>
|
||||
</form>
|
||||
<form action="/{{ currentLocale }}/booking" method="get" class="campsite_type_booking">
|
||||
<input type="hidden" name="campsite_type" value="{{ .Slug }}">
|
||||
<fieldset>
|
||||
<label>
|
||||
{{( pgettext "Check-in Date" "input")}}
|
||||
<br>
|
||||
<input name="arrival_date" type="date" required>
|
||||
<br>
|
||||
</label>
|
||||
<label>
|
||||
{{( pgettext "Check-out Date" "input")}}
|
||||
<br>
|
||||
<input name="departure_date" type="date" required>
|
||||
<br>
|
||||
</label>
|
||||
</fieldset>
|
||||
<footer>
|
||||
<button type="submit">{{( pgettext "Book" "action" )}} <span>→</span></button>
|
||||
</footer>
|
||||
</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 -}}
|
||||
<article class="campsite_type_prices">
|
||||
<h3 class="sr-only">{{( pgettext "Prices" "title" )}}</h3>
|
||||
<dl>
|
||||
{{ range . -}}
|
||||
<div>
|
||||
<dt>
|
||||
<svg width="20px" height="20px">
|
||||
<circle cx="50%" cy="50%" r="49%" fill="{{ .SeasonColor }}" stroke="#000"
|
||||
stroke-width=".5"/>
|
||||
<div x-data="{open: false}">
|
||||
<dt @click="open = !open" :class="open && 'open'">
|
||||
<svg width="30px" height="30px">
|
||||
<circle cx="50%" cy="50%" r="49%" fill="{{ .SeasonColor }}"/>
|
||||
</svg>
|
||||
{{ .SeasonName }}
|
||||
</dt>
|
||||
{{ if .HasOptions -}}
|
||||
<dd>{{ printf (gettext "Starting from %s €/night") .PricePerNight }}</dd>
|
||||
{{- else if .PricePerNight -}}
|
||||
<dd>{{ printf (gettext "%s €/night") .PricePerNight }}</dd>
|
||||
<dd x-show="open">{{ printf (gettext "%s €/night") .PricePerNight }}</dd>
|
||||
{{ range .Options }}
|
||||
<dd x-show="open">{{ printf (gettext "%s: %s €/night") .OptionName .PricePerNight }}</dd>
|
||||
{{- end }}
|
||||
{{ 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 }}
|
||||
</div>
|
||||
{{- end }}
|
||||
|
@ -83,18 +94,6 @@
|
|||
{{- end }}
|
||||
</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 -}}
|
||||
<article class="campsite_type_features">
|
||||
<h3 class="sr-only">{{( pgettext "Features" "title" )}}</h3>
|
||||
|
|
Loading…
Reference in New Issue