diff --git a/apps/tpanel/src/routes/panel/price/+page.svelte b/apps/tpanel/src/routes/panel/price/+page.svelte index 04db53e..de4f2a2 100644 --- a/apps/tpanel/src/routes/panel/price/+page.svelte +++ b/apps/tpanel/src/routes/panel/price/+page.svelte @@ -1,20 +1,20 @@
{#each package_combinations as package_combination} {@const price = get_price(package_combination)} - {@const default_price = get_offer_price(empty_offer, package_combination)} + {@const default_price = get_statt_price_string(package_combination)}
- {package_combination.join(', ')} = {@html price.jahr.toFixed(2)} (default: {@html default_price.jahr.toFixed( - 2 - )}) => {@html price.monat.toFixed(2)} + {package_combination.join(', ')} = {@html price.jahr.toFixed(2)} + {#if default_price} + (statt: {@html default_price}) + {/if}, monat: {@html price.monat.toFixed(2)} {@html get_offer_note(package_combination)}
diff --git a/packages/asset_library/assets/packages.ts b/packages/asset_library/assets/packages.ts index 65113a4..3733cde 100644 --- a/packages/asset_library/assets/packages.ts +++ b/packages/asset_library/assets/packages.ts @@ -21,7 +21,8 @@ const packages_image_location = '/images/assets/packages/'; export const packages_assets = [ { id: 'entertainment', - price: { jahr: 16.5, monat: 19, singular: 0 }, + // price: { jahr: 16.5, monat: 19, singular: 0 }, + price: { jahr: 18.5, monat: 19, singular: 0 }, name: 'Entertainment', note: 'Exklusives Entertainment ohne Ende.', image: { @@ -45,7 +46,8 @@ export const packages_assets = [ }, { id: 'entertainmentplus', - price: { jahr: 21.5, monat: 27.5, singular: 0 }, + // price: { jahr: 21.5, monat: 27.5, singular: 0 }, + price: { jahr: 27.5, monat: 27.5, singular: 0 }, name: 'Entertainment Plus', note: 'Alle Sky Serien und Netflix.', image: { diff --git a/packages/asset_library/prices.ts b/packages/asset_library/prices.ts index 1d51f11..f854df2 100644 --- a/packages/asset_library/prices.ts +++ b/packages/asset_library/prices.ts @@ -1,67 +1,49 @@ -import type { package_id } from "./assets/packages"; -import { indexed_priceable_assets } from "./priceable_asset"; -import type { Price } from "./priceable_asset_types"; -import type { priceable_asset_id } from "./asset_types"; -import { - map_entries, - typed_entries, - typed_from_entries, -} from "functional-utilities"; -import { - clone, - intersection as intersect, - isEqual, - minBy, - sortBy, - sum, -} from "lodash-es"; -import { - empty_offer, - indexed_offers, - offer_applicable, - offer_ids, -} from "./offer_description"; -import type { offer_id, offer_description_type } from "./offer_description"; -import { asset_sets } from "./sets"; -import { zubuchoption_ids } from "./assets/zubuchoptionen"; -import type { zubuchoption_id } from "./assets/zubuchoptionen"; +import type { package_id } from './assets/packages'; +import { indexed_priceable_assets } from './priceable_asset'; +import type { Price } from './priceable_asset_types'; +import type { priceable_asset_id } from './asset_types'; +import { map_entries, typed_entries, typed_from_entries } from 'functional-utilities'; +import { clone, intersection as intersect, isEqual, minBy, sortBy, sum } from 'lodash-es'; +import { empty_offer, indexed_offers, offer_applicable, offer_ids } from './offer_description'; +import type { offer_id, offer_description_type } from './offer_description'; +import { asset_sets } from './sets'; +import { zubuchoption_ids } from './assets/zubuchoptionen'; +import type { zubuchoption_id } from './assets/zubuchoptionen'; export function to_price_string(v: number, escaped: boolean = true): string { - let str = `€${escaped ? " " : " "}` + v.toFixed(2).replace(".", ","); - if (str.endsWith(",00")) { + let str = `€${escaped ? ' ' : ' '}` + v.toFixed(2).replace('.', ','); + if (str.endsWith(',00')) { str = str.slice(0, -3); } return str; } +const well_defined_combinations = [ + ['entertainment', 'sport', 'bundesliga', 'cinema'], + ['entertainmentplus', 'sport', 'bundesliga', 'cinema'] +] satisfies ReadonlyArray>; + export const aktivierung = 0 as number; export const aktivierung_string = to_price_string(aktivierung); export const bonus = 20 as number; -export const bonus_string = (escaped: boolean = true) => - to_price_string(bonus, escaped); +export const bonus_string = (escaped: boolean = true) => to_price_string(bonus, escaped); -const price_table = map_entries(indexed_priceable_assets, ([key, value]) => [ - key, - value.price, -]); +const price_table = map_entries(indexed_priceable_assets, ([key, value]) => [key, value.price]); export function get_offer_price( offer: offer_description_type, assets: ReadonlyArray, - exclude_overwrite?: boolean, + exclude_overwrite?: boolean ): Price { const current_price_table = clone(price_table); for (const overwrite of offer.overwrites) { const matches = (id: priceable_asset_id) => - zubuchoption_ids.includes(id as zubuchoption_id) || id === "kids"; + zubuchoption_ids.includes(id as zubuchoption_id) || id === 'kids'; const filtered_zubuchoptionen = assets.filter(matches); const other_assets = assets.filter((id) => !matches(id)); - if ( - isEqual(sortBy(overwrite[0]), sortBy(other_assets)) && - !exclude_overwrite - ) { + if (isEqual(sortBy(overwrite[0]), sortBy(other_assets)) && !exclude_overwrite) { const price = (() => { if (isEqual(empty_offer, offer)) { return get_offer_price(empty_offer, assets, true); @@ -69,10 +51,7 @@ export function get_offer_price( return get_offer_price(empty_offer, assets, false); } })(); - const zubuchoptionen_price = get_offer_price( - empty_offer, - filtered_zubuchoptionen, - ); + const zubuchoptionen_price = get_offer_price(empty_offer, filtered_zubuchoptionen); for (const [timeframe, value] of typed_entries(overwrite[1])) { price[timeframe] = value + zubuchoptionen_price[timeframe]; } @@ -90,28 +69,51 @@ export function get_offer_price( current_price_table[asset_id] = typed_from_entries( typed_entries(current_price_table[asset_id]).map(([key, value]) => [ key, - key === "jahr" - ? operation(action.value[key], value as number) - : value, - ]), + key === 'jahr' ? operation(action.value[key], value as number) : value + ]) ); }); }); return typed_from_entries( - (["singular", "monat", "jahr"] as const).map((key) => [ + (['singular', 'monat', 'jahr'] as const).map((key) => [ key, - sum(assets.map((asset_id) => current_price_table[asset_id][key])), - ]), + sum(assets.map((asset_id) => current_price_table[asset_id][key])) + ]) ); } -function chose_offer( - assets: ReadonlyArray, -): offer_id | undefined { - const offers = offer_ids.filter((offer_id) => - offer_applicable(offer_id, assets), - ); +function are_same_assets( + a: ReadonlyArray, + b: ReadonlyArray +) { + return isEqual([...a].sort(), [...b].sort()); +} + +function get_statt_price(assets: ReadonlyArray): number | undefined { + const base_price = get_offer_price(empty_offer, assets).jahr; + const ideal_price = get_price(assets).jahr; + if ( + ideal_price < base_price && + well_defined_combinations.some((combination) => are_same_assets(assets, combination)) + ) { + return base_price; + } + return undefined; +} + +export function get_statt_price_string( + assets: ReadonlyArray +): string | undefined { + const statt_price = get_statt_price(assets); + if (statt_price === undefined) { + return undefined; + } + return `${to_price_string(statt_price)}`; +} + +function chose_offer(assets: ReadonlyArray): offer_id | undefined { + const offers = offer_ids.filter((offer_id) => offer_applicable(offer_id, assets)); if (offers.length === 0) { return undefined; } @@ -119,11 +121,8 @@ function chose_offer( (offer_id) => [ offer_id, - get_offer_price( - offer_id ? indexed_offers[offer_id] : empty_offer, - assets, - ), - ] as const, + get_offer_price(offer_id ? indexed_offers[offer_id] : empty_offer, assets) + ] as const ); const base_price = get_offer_price(empty_offer, assets); const min = minBy(prices, ([, price]) => price.jahr); @@ -134,11 +133,11 @@ function chose_offer( } export function get_price(assets: ReadonlyArray): Price { - const chosen_offer = 'opt1' + const chosen_offer = 'opt1'; const offer = chosen_offer ? indexed_offers[chosen_offer] : empty_offer; const offer_price = get_offer_price(offer, assets); if (offer_price === undefined) { - throw new Error("Internal Error, chose invalid offer"); + throw new Error('Internal Error, chose invalid offer'); } else { return offer_price; } @@ -147,36 +146,29 @@ export function get_price(assets: ReadonlyArray): Price { export function get_price_string( assets: ReadonlyArray, subscription_time: keyof Price, - escaped: boolean = true, + escaped: boolean = true ): string { return to_price_string(get_price(assets)[subscription_time], escaped); } -export function sort_by_price( - lst: ReadonlyArray, -): Array { +export function sort_by_price(lst: ReadonlyArray): Array { return [...lst].sort( - (a, b) => - indexed_priceable_assets[b].price.jahr - - indexed_priceable_assets[a].price.jahr, + (a, b) => indexed_priceable_assets[b].price.jahr - indexed_priceable_assets[a].price.jahr ); } function get_offer_savings_string( offer: offer_description_type, - ids: ReadonlyArray, + ids: ReadonlyArray ): string { const actual_savings = - (get_offer_price(empty_offer, ids).jahr - - get_offer_price(offer, ids).jahr) * - 12 + - 60; + (get_offer_price(empty_offer, ids).jahr - get_offer_price(offer, ids).jahr) * 12; const overwrites: [package_id[], number][] = [ // [['entertainment', 'cinema', 'sport', 'bundesliga'], 240], // [['entertainmentplus', 'cinema', 'sport', 'bundesliga'], 288] ]; const savings = overwrites.find(([packages]) => - isEqual(sort_by_price(packages), sort_by_price(ids)), + isEqual(sort_by_price(packages), sort_by_price(ids)) ); if (savings) { return to_price_string(savings[1]); @@ -184,9 +176,7 @@ function get_offer_savings_string( return to_price_string(actual_savings); } -export function get_savings_string( - ids: ReadonlyArray, -): string { +export function get_savings_string(ids: ReadonlyArray): string { const chosen_offer = chose_offer(ids); if (chosen_offer === undefined) { return to_price_string(0); @@ -194,19 +184,15 @@ export function get_savings_string( return get_offer_savings_string(indexed_offers[chosen_offer], ids); } -export function get_offer_note( - packages: ReadonlyArray, - long = false, -): string { +export function get_offer_note(packages: ReadonlyArray, long = false): string { const chosen_offer = chose_offer(packages); - if (chosen_offer === undefined) { - return ""; + if ( + chosen_offer !== undefined && + well_defined_combinations.some((combination) => are_same_assets(packages, combination)) + ) { + const offer = indexed_offers[chosen_offer]; + const text = long ? offer.long_text : offer.short_text; + return text.replaceAll('{savings}', get_offer_savings_string(offer, packages)); } - return "" - const offer = indexed_offers[chosen_offer]; - const text = long ? offer.long_text : offer.short_text; - return text.replaceAll( - "{savings}", - get_offer_savings_string(offer, packages), - ); + return ''; } diff --git a/packages/components/complete/package_table.svelte b/packages/components/complete/package_table.svelte index 3a54892..5cf2061 100644 --- a/packages/components/complete/package_table.svelte +++ b/packages/components/complete/package_table.svelte @@ -1,10 +1,5 @@
@@ -71,8 +70,8 @@ 12 Monate nur {@html to_price_string(primary_price)} mtl.*

- {#if base_price > primary_price} - statt {@html to_price_string(base_price)} mtl. + {#if base_price} + statt {@html base_price} mtl.
{/if} (im Jahres-Abo, danach {@html get_price_string(price_asset_ids, 'monat')} mtl.* im Monats-Abo) diff --git a/packages/config/global.scss b/packages/config/global.scss index 7d0d26c..38272db 100644 --- a/packages/config/global.scss +++ b/packages/config/global.scss @@ -39,17 +39,16 @@ body { @media screen and (max-width: 970px) { .showcase_size { - height: 950px + height: 950px; } } @media screen and (max-width: 500px) { .showcase_size { - height: 1000px + height: 1000px; } } - $gradient-safezone: 5px; .gradient_text { @@ -62,12 +61,12 @@ $gradient-safezone: 5px; .color_text { background: linear-gradient( - 142deg, - rgb(253, 122, 29) 0%, - rgb(253, 29, 29) 21%, - rgb(194, 64, 159) 51%, - rgb(106, 92, 186) 77%, - rgb(29, 162, 253) 100% + to right, + rgb(245, 100, 0) 0%, + rgb(255, 0, 0) 25%, + rgb(181, 0, 125) 50%, + rgb(33, 66, 156) 75%, + rgb(0, 113, 255) 100% ); -webkit-background-clip: text; background-clip: text;