Skip to content

Commit

Permalink
add savings
Browse files Browse the repository at this point in the history
  • Loading branch information
JeremyMoeglich committed Jun 11, 2024
1 parent 4223d47 commit 61b0faf
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 129 deletions.
12 changes: 6 additions & 6 deletions apps/tpanel/src/routes/panel/price/+page.svelte
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
<script lang="ts">
import { package_combinations } from 'asset_library/assets/packages';
import { empty_offer } from 'asset_library/offer_description';
import { get_offer_note, get_offer_price, get_price } from 'asset_library/prices';
import { get_offer_note, get_statt_price_string, get_price } from 'asset_library/prices';
</script>

<div class="flex flex-col p-8 text-slate-300">
{#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)}

<div>
<div>
{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)}
</div>
</div>
Expand Down
6 changes: 4 additions & 2 deletions packages/asset_library/assets/packages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand All @@ -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: {
Expand Down
174 changes: 80 additions & 94 deletions packages/asset_library/prices.ts
Original file line number Diff line number Diff line change
@@ -1,78 +1,57 @@
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 ? "&nbsp" : " "}` + v.toFixed(2).replace(".", ",");
if (str.endsWith(",00")) {
let str = `€${escaped ? '&nbsp' : ' '}` + 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<ReadonlyArray<package_id>>;

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<priceable_asset_id>,
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);
} else {
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];
}
Expand All @@ -90,40 +69,60 @@ 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<priceable_asset_id>,
): offer_id | undefined {
const offers = offer_ids.filter((offer_id) =>
offer_applicable(offer_id, assets),
);
function are_same_assets(
a: ReadonlyArray<priceable_asset_id>,
b: ReadonlyArray<priceable_asset_id>
) {
return isEqual([...a].sort(), [...b].sort());
}

function get_statt_price(assets: ReadonlyArray<priceable_asset_id>): 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<priceable_asset_id>
): 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<priceable_asset_id>): offer_id | undefined {
const offers = offer_ids.filter((offer_id) => offer_applicable(offer_id, assets));
if (offers.length === 0) {
return undefined;
}
const prices = offers.map(
(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);
Expand All @@ -134,11 +133,11 @@ function chose_offer(
}

export function get_price(assets: ReadonlyArray<priceable_asset_id>): 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;
}
Expand All @@ -147,66 +146,53 @@ export function get_price(assets: ReadonlyArray<priceable_asset_id>): Price {
export function get_price_string(
assets: ReadonlyArray<priceable_asset_id>,
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<priceable_asset_id>,
): Array<priceable_asset_id> {
export function sort_by_price(lst: ReadonlyArray<priceable_asset_id>): Array<priceable_asset_id> {
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<priceable_asset_id>,
ids: ReadonlyArray<priceable_asset_id>
): 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]);
}
return to_price_string(actual_savings);
}

export function get_savings_string(
ids: ReadonlyArray<priceable_asset_id>,
): string {
export function get_savings_string(ids: ReadonlyArray<priceable_asset_id>): string {
const chosen_offer = chose_offer(ids);
if (chosen_offer === undefined) {
return to_price_string(0);
}
return get_offer_savings_string(indexed_offers[chosen_offer], ids);
}

export function get_offer_note(
packages: ReadonlyArray<package_id>,
long = false,
): string {
export function get_offer_note(packages: ReadonlyArray<package_id>, 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 '';
}
24 changes: 11 additions & 13 deletions packages/components/complete/package_table.svelte
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
<script lang="ts">
import {
get_offer_note,
get_offer_price,
get_price_string,
to_price_string
} from 'asset_library/prices';
import { get_offer_note, get_statt_price_string, get_price_string } from 'asset_library/prices';
import {
max_combination_length,
base_premium_package_combinations,
Expand All @@ -17,7 +12,6 @@
import { dev } from '$app/environment';
import { make_url } from 'frontend/url';
import { typed_entries } from 'functional-utilities';
import { empty_offer } from 'asset_library/offer_description';
const col_amount = max_combination_length + 1;
Expand Down Expand Up @@ -64,18 +58,22 @@
{/each}
<div class="cell text-cell" style:--row={row_index + 1} style:--col={col_amount}>
<div class="text-cell-inner">
{#if get_offer_note(row)}
<span class="nowrap color_text" style="font-size:15px;padding-bottom: 15px;letter-spacing:.2px;"><b>{@html get_offer_note(row)}</b></span>
{/if}
<p style="line-height: 23px;color:#ff4444;font-size:20px;">
<b>
{#if get_offer_note(row)}
<mark class="nowrap">{@html get_offer_note(row)}</mark>
<br />
{/if}

{@html get_price_string(row, 'jahr')}&nbsp;mtl.*
</b>
</p>
<p class="small">
<!-- statt: {@html to_price_string(get_offer_price(empty_offer, row).jahr, true)} mtl. <br /> -->
<!-- {@html (() => {
const statt = get_statt_price_string(row);
if (statt) {
return `statt: ${statt}<br>`;
}
return '';
})()} -->
(im Jahres-Abo, danach {@html get_price_string(row, 'monat')}&nbsp;mtl. im Monats-Abo)
</p>
</div>
Expand Down
Loading

0 comments on commit 61b0faf

Please sign in to comment.