Initial commit
This commit is contained in:
@@ -0,0 +1,249 @@
|
||||
export type Price = number | string;
|
||||
|
||||
export interface PlanPage {
|
||||
code: string;
|
||||
landing: Price;
|
||||
webApp: Price;
|
||||
care: [Price, Price, Price];
|
||||
}
|
||||
|
||||
export function getPlanPages(): PlanPage[] {
|
||||
const raw = process.env.PLANS_PAGES;
|
||||
if (!raw) return [];
|
||||
try {
|
||||
const parsed = JSON.parse(raw) as unknown;
|
||||
if (!Array.isArray(parsed)) return [];
|
||||
return parsed.filter(
|
||||
(p): p is PlanPage =>
|
||||
!!p && typeof p.code === "string" && Array.isArray(p.care),
|
||||
);
|
||||
} catch (err) {
|
||||
console.error(
|
||||
"PLANS_PAGES is not valid JSON — no pricing pages will render.",
|
||||
err,
|
||||
);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
export function getPlanPage(code: string): PlanPage | null {
|
||||
return getPlanPages().find((p) => p.code === code) ?? null;
|
||||
}
|
||||
|
||||
function euro(value: Price, isSR: boolean): string {
|
||||
if (typeof value !== "number") return value;
|
||||
const n = new Intl.NumberFormat(isSR ? "sr-RS" : "en-US", {
|
||||
maximumFractionDigits: 0,
|
||||
}).format(value);
|
||||
return `€${n}`;
|
||||
}
|
||||
|
||||
function fromPrice(value: Price, isSR: boolean): string {
|
||||
if (typeof value !== "number") return value;
|
||||
return `${isSR ? "od " : "from "}${euro(value, isSR)}`;
|
||||
}
|
||||
|
||||
function carePrice(value: Price, isSR: boolean): string {
|
||||
if (typeof value !== "number") return value;
|
||||
return `${euro(value, isSR)}${isSR ? "/mj" : "/mo"}`;
|
||||
}
|
||||
|
||||
export function getPlansDict(locale: string, page: PlanPage) {
|
||||
const isSR = locale === "sr";
|
||||
|
||||
const landing = fromPrice(page.landing, isSR);
|
||||
const webApp = fromPrice(page.webApp, isSR);
|
||||
const care = page.care.map((c) => carePrice(c, isSR)) as [
|
||||
string,
|
||||
string,
|
||||
string,
|
||||
];
|
||||
|
||||
if (isSR) {
|
||||
return {
|
||||
badge: "Za klijente",
|
||||
title: "Paketi i",
|
||||
titleAccent: "cijene.",
|
||||
subtitle:
|
||||
"Okvirne početne cijene za najčešće tipove projekata. Svaki projekat dobija prilagođenu ponudu nakon kratkog razgovora — ova stranica služi da dobijete realan okvir.",
|
||||
tiersTitle: "Cijene projekata",
|
||||
popularBadge: "Najtraženije",
|
||||
tiers: [
|
||||
{
|
||||
name: "Landing / prezentacioni sajt",
|
||||
price: landing,
|
||||
period: "jednokratno",
|
||||
description:
|
||||
"Brz, SEO-spreman sajt za vaš biznis — dizajniran, izrađen, deployovan i hostovan.",
|
||||
features: [
|
||||
"Custom dizajn, responzivan na svim uređajima",
|
||||
"SEO podešavanje, analitika, kontakt forma",
|
||||
"Deploy sa SSL-om na upravljanoj infrastrukturi",
|
||||
"Isporuka za 1–2 sedmice",
|
||||
],
|
||||
highlighted: false,
|
||||
},
|
||||
{
|
||||
name: "Web aplikacija",
|
||||
price: webApp,
|
||||
period: "jednokratno",
|
||||
description:
|
||||
"Custom web aplikacija sa backendom, bazom podataka i admin panelom — prilagođena vašem načinu rada.",
|
||||
features: [
|
||||
"Frontend, backend i dizajn baze podataka",
|
||||
"Autentifikacija i upravljanje ulogama",
|
||||
"Admin panel i interni alati",
|
||||
"CI/CD, monitoring i backup uključeni",
|
||||
"Isporuka za 4–8 sedmica u zavisnosti od obima",
|
||||
],
|
||||
highlighted: true,
|
||||
},
|
||||
{
|
||||
name: "Custom platforma",
|
||||
price: "po dogovoru",
|
||||
period: "zajedničko planiranje",
|
||||
description:
|
||||
"Veći proizvodi: e-commerce, SaaS, mobilna + web aplikacija. Obim i cijena se definišu nakon analize.",
|
||||
features: [
|
||||
"Sve iz paketa Web aplikacija",
|
||||
"Mobilna aplikacija (React Native) po potrebi",
|
||||
"Plaćanja, integracije, eksterni API-ji",
|
||||
"Dugoročna razvojna saradnja",
|
||||
],
|
||||
highlighted: false,
|
||||
},
|
||||
],
|
||||
careTitle: "Mjesečni paketi održavanja",
|
||||
careSubtitle:
|
||||
"Opcioni mjesečni paketi nakon lansiranja, da vaš projekat ostane brz, siguran i dostupan.",
|
||||
care: [
|
||||
{
|
||||
name: "Hosting",
|
||||
price: care[0],
|
||||
features: [
|
||||
"Upravljani hosting sa SSL-om",
|
||||
"Dnevni backup",
|
||||
"Monitoring dostupnosti",
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Standard",
|
||||
price: care[1],
|
||||
features: [
|
||||
"Sve iz paketa Hosting",
|
||||
"Izmjene sadržaja i manje dorade",
|
||||
"Sigurnosna ažuriranja",
|
||||
"Prioritetna email podrška",
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Priority",
|
||||
price: care[2],
|
||||
features: [
|
||||
"Sve iz paketa Standard",
|
||||
"Mjesečni sati za razvoj funkcionalnosti",
|
||||
"Odgovor isti dan",
|
||||
"Pregledi performansi",
|
||||
],
|
||||
},
|
||||
],
|
||||
disclaimer:
|
||||
"Cijene su početne, ne fiksne ponude — konačna cijena zavisi od obima i potvrđuje se pisanom ponudom prije početka rada.",
|
||||
cta: "Razgovarajmo o projektu",
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
badge: "For clients",
|
||||
title: "Plans &",
|
||||
titleAccent: "pricing.",
|
||||
subtitle:
|
||||
"Indicative starting prices for the most common project types. Every project gets a tailored quote after a short discovery call — this page is just to give you a realistic frame.",
|
||||
tiersTitle: "Project pricing",
|
||||
popularBadge: "Popular",
|
||||
tiers: [
|
||||
{
|
||||
name: "Landing / marketing site",
|
||||
price: landing,
|
||||
period: "one-time",
|
||||
description:
|
||||
"A fast, SEO-ready site for your business — designed, built, deployed, and hosted.",
|
||||
features: [
|
||||
"Custom design, responsive on all devices",
|
||||
"SEO setup, analytics, contact form",
|
||||
"Deployed with SSL on managed infrastructure",
|
||||
"Delivered in 1–2 weeks",
|
||||
],
|
||||
highlighted: false,
|
||||
},
|
||||
{
|
||||
name: "Web application",
|
||||
price: webApp,
|
||||
period: "one-time",
|
||||
description:
|
||||
"A custom web app with backend, database, and admin panel — built around your workflow.",
|
||||
features: [
|
||||
"Frontend, backend, and database design",
|
||||
"Authentication and role management",
|
||||
"Admin dashboard and internal tools",
|
||||
"CI/CD, monitoring, and backups included",
|
||||
"Delivered in 4–8 weeks depending on scope",
|
||||
],
|
||||
highlighted: true,
|
||||
},
|
||||
{
|
||||
name: "Custom platform",
|
||||
price: "custom quote",
|
||||
period: "scoped together",
|
||||
description:
|
||||
"Larger products: e-commerce, SaaS, mobile + web. Scoped and priced after discovery.",
|
||||
features: [
|
||||
"Everything in Web application",
|
||||
"Mobile app (React Native) if needed",
|
||||
"Payments, integrations, third-party APIs",
|
||||
"Long-term development partnership",
|
||||
],
|
||||
highlighted: false,
|
||||
},
|
||||
],
|
||||
careTitle: "Ongoing care plans",
|
||||
careSubtitle:
|
||||
"Optional monthly plans after launch, so your project stays fast, secure, and online.",
|
||||
care: [
|
||||
{
|
||||
name: "Hosting",
|
||||
price: care[0],
|
||||
features: [
|
||||
"Managed hosting with SSL",
|
||||
"Daily backups",
|
||||
"Uptime monitoring",
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Standard",
|
||||
price: care[1],
|
||||
features: [
|
||||
"Everything in Hosting",
|
||||
"Content and minor updates",
|
||||
"Security and dependency updates",
|
||||
"Priority email support",
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Priority",
|
||||
price: care[2],
|
||||
features: [
|
||||
"Everything in Standard",
|
||||
"Monthly feature-development hours",
|
||||
"Same-day response",
|
||||
"Performance reviews",
|
||||
],
|
||||
},
|
||||
],
|
||||
disclaimer:
|
||||
"Prices are starting points, not fixed quotes — final pricing depends on scope and is confirmed in a written proposal before any work begins.",
|
||||
cta: "Discuss your project",
|
||||
};
|
||||
}
|
||||
|
||||
export type PlansDict = ReturnType<typeof getPlansDict>;
|
||||
Reference in New Issue
Block a user