diff --git a/app/[lang]/(hyperjump)/case-studies/[slug]/components/content.tsx b/app/[lang]/(hyperjump)/case-studies/[slug]/components/content.tsx index b29aff160..350437d5d 100644 --- a/app/[lang]/(hyperjump)/case-studies/[slug]/components/content.tsx +++ b/app/[lang]/(hyperjump)/case-studies/[slug]/components/content.tsx @@ -8,31 +8,32 @@ import { CaseStudiesVolunteeringPlatform } from "@/locales/.generated/locales-markdown"; import type { SupportedLanguage } from "@/locales/.generated/types"; +import { CaseStudySlug } from "../../../data"; type ContentProps = { lang: SupportedLanguage; slug: string; }; -const caseStudies = [ - { - slug: "erp-fisheries", - content: CaseStudiesFisheries - }, - { - slug: "ctoaas-media", - content: CaseStudiesMedia - }, - { - slug: "saas-volunteering-platform", - content: CaseStudiesVolunteeringPlatform - } -]; - export function Content({ lang, slug }: ContentProps) { + const caseStudies = [ + { + slug: CaseStudySlug.Fisheries, + content: CaseStudiesFisheries + }, + { + slug: CaseStudySlug.Media, + content: CaseStudiesMedia + }, + { + slug: CaseStudySlug.VolunteeringPlatform, + content: CaseStudiesVolunteeringPlatform + } + ]; const caseStudy = caseStudies.find((caseStudy) => caseStudy.slug === slug); if (!caseStudy) { + console.error(`Case study content for "${slug}" is not registered.`); notFound(); } diff --git a/app/[lang]/(hyperjump)/case-studies/[slug]/page.tsx b/app/[lang]/(hyperjump)/case-studies/[slug]/page.tsx index 120d7e4f5..87f90dd81 100644 --- a/app/[lang]/(hyperjump)/case-studies/[slug]/page.tsx +++ b/app/[lang]/(hyperjump)/case-studies/[slug]/page.tsx @@ -15,8 +15,12 @@ import { import type { SupportedLanguage } from "@/locales/.generated/types"; import { supportedLanguages } from "@/locales/.generated/types"; -import type { CaseStudy } from "../../data"; -import { caseStudyBy, getCaseStudies } from "../data"; +import { + caseStudyBy, + getCaseStudies, + serviceBySlug, + type CaseStudy +} from "../../data"; import { Content } from "./components/content"; type Params = { lang: SupportedLanguage; slug: string }; @@ -30,7 +34,7 @@ export async function generateMetadata({ }: CaseStudyProps): Promise { const { lang, slug } = await params; const { url } = data; - const caseStudies = caseStudyBy(slug, lang); + const caseStudies = caseStudyBy({ lang, slug }); const meta: Metadata = { title: `Case-Studies - ${caseStudies?.title ?? ""}`, description: caseStudies?.description ?? "", @@ -60,7 +64,7 @@ export async function generateStaticParams(): Promise { export default async function CaseStudy({ params }: CaseStudyProps) { const { lang, slug } = await params; - const caseStudy = caseStudyBy(slug, lang); + const caseStudy = caseStudyBy({ lang, slug }); if (!caseStudy) { notFound(); } @@ -140,13 +144,13 @@ function Recommendation({ caseStudies, lang }: RecommendationProps) { {caseStudyMore(lang)}
- {caseStudies.map(({ description, slug, title, category }) => ( + {caseStudies.map(({ description, serviceSlug, slug, title }) => (
- {category} + {serviceBySlug({ lang, slug: serviceSlug })?.title}

{title} diff --git a/app/[lang]/(hyperjump)/case-studies/data.ts b/app/[lang]/(hyperjump)/case-studies/data.ts deleted file mode 100644 index f74492ab8..000000000 --- a/app/[lang]/(hyperjump)/case-studies/data.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { - caseStudyCtoaasMediaCategory, - caseStudyCtoaasMediaDesc, - caseStudyCtoaasMediaTitle, - caseStudyErpFisheriesCategory, - caseStudyErpFisheriesDesc, - caseStudyErpFisheriesTitle, - caseStudyCtoaasMediaCtaHeading, - caseStudyCtoaasMediaCtaLabel, - caseStudyCtoaasMediaCtaSubject, - caseStudyErpFisheriesCtaHeading, - caseStudyErpFisheriesCtaLabel, - caseStudyErpFisheriesCtaSubject, - caseStudySaasVolunteeringPlatformTitle, - caseStudySaasVolunteeringPlatformDesc, - caseStudySaasVolunteeringPlatformCategory, - caseStudySaasVolunteeringPlatformCtaHeading, - caseStudySaasVolunteeringPlatformCtaSubject, - caseStudySaasVolunteeringPlatformCtaLabel -} from "@/locales/.generated/server"; -import type { SupportedLanguage } from "@/locales/.generated/types"; - -export const getCaseStudies = (lang: SupportedLanguage) => { - return [ - { - slug: "erp-fisheries", - title: caseStudyErpFisheriesTitle(lang), - description: caseStudyErpFisheriesDesc(lang), - category: caseStudyErpFisheriesCategory(lang), - cta: { - heading: caseStudyErpFisheriesCtaHeading(lang), - subject: caseStudyErpFisheriesCtaSubject(lang), - label: caseStudyErpFisheriesCtaLabel(lang) - } - }, - { - slug: "ctoaas-media", - title: caseStudyCtoaasMediaTitle(lang), - description: caseStudyCtoaasMediaDesc(lang), - category: caseStudyCtoaasMediaCategory(lang), - cta: { - heading: caseStudyCtoaasMediaCtaHeading(lang), - subject: caseStudyCtoaasMediaCtaSubject(lang), - label: caseStudyCtoaasMediaCtaLabel(lang) - } - }, - { - slug: "saas-volunteering-platform", - title: caseStudySaasVolunteeringPlatformTitle(lang), - description: caseStudySaasVolunteeringPlatformDesc(lang), - category: caseStudySaasVolunteeringPlatformCategory(lang), - cta: { - heading: caseStudySaasVolunteeringPlatformCtaHeading(lang), - subject: caseStudySaasVolunteeringPlatformCtaSubject(lang), - label: caseStudySaasVolunteeringPlatformCtaLabel(lang) - } - } - ]; -}; - -export function caseStudyBy(slug: string, lang: SupportedLanguage) { - return getCaseStudies(lang).find((cs) => cs.slug === slug); -} diff --git a/app/[lang]/(hyperjump)/case-studies/page.tsx b/app/[lang]/(hyperjump)/case-studies/page.tsx index efa3828e7..26a2c31a8 100644 --- a/app/[lang]/(hyperjump)/case-studies/page.tsx +++ b/app/[lang]/(hyperjump)/case-studies/page.tsx @@ -1,8 +1,6 @@ -import Link from "next/link"; import type { Metadata } from "next"; import { Hero } from "@/app/components/hero"; -import { Button } from "@/components/ui/button"; import data from "@/data.json"; import { dynamicOpengraph } from "@/lib/default-metadata"; import { @@ -10,14 +8,14 @@ import { type SupportedLanguage } from "@/locales/.generated/types"; import { - caseStudyButton, caseStudyExplore, caseStudyHeroDesc, caseStudyHeroHeading, caseStudyTitle } from "@/locales/.generated/strings"; -import { getCaseStudies } from "./data"; +import { getCaseStudies } from "../data"; +import { CaseStudyCard } from "../components/case-study-card"; export const generateStaticParams = async () => { return supportedLanguages.map((lang) => ({ lang })); @@ -63,47 +61,20 @@ export default async function CaseStudiesPage({ params }: CaseStudyProps) {

{caseStudyExplore(lang)}

- +
+
+
+ {getCaseStudies(lang).map((caseStudy) => ( + + ))} +
+
+
); } - -function CaseStudies({ lang }: { lang: SupportedLanguage }) { - return ( -
-
-
- {getCaseStudies(lang).map( - ({ category, description, slug, title }) => ( -
-
- - {category} - -

- {title} -

-

- {description} -

-
- - -
- ) - )} -
-
-
- ); -} diff --git a/app/[lang]/(hyperjump)/components/case-study-card.tsx b/app/[lang]/(hyperjump)/components/case-study-card.tsx new file mode 100644 index 000000000..b8e3f623c --- /dev/null +++ b/app/[lang]/(hyperjump)/components/case-study-card.tsx @@ -0,0 +1,43 @@ +import Link from "next/link"; + +import { Button } from "@/components/ui/button"; +import { caseStudyButton } from "@/locales/.generated/strings"; +import type { SupportedLanguage } from "@/locales/.generated/types"; + +import type { CaseStudy } from "../data"; +import { serviceBySlug } from "../data"; + +type CaseStudyCardProps = { + caseStudy: CaseStudy; + lang: SupportedLanguage; +}; + +export function CaseStudyCard({ + caseStudy: { description, serviceSlug, slug, title, url }, + lang +}: CaseStudyCardProps) { + return ( +
+
+ + {serviceBySlug({ lang, slug: serviceSlug })?.title} + +

+ {title} +

+

+ {description} +

+
+ + +
+ ); +} diff --git a/app/[lang]/(hyperjump)/data.ts b/app/[lang]/(hyperjump)/data.ts index d62a5c019..3fa7dc30b 100644 --- a/app/[lang]/(hyperjump)/data.ts +++ b/app/[lang]/(hyperjump)/data.ts @@ -57,12 +57,21 @@ import { aiWhyUsReasons0, aiWhyUsReasons1, aiWhyUsReasons2, - caseStudyCtoaasMediaCategory, + caseStudyCtoaasMediaCtaHeading, + caseStudyCtoaasMediaCtaLabel, + caseStudyCtoaasMediaCtaSubject, caseStudyCtoaasMediaDesc, caseStudyCtoaasMediaTitle, - caseStudyErpFisheriesCategory, + caseStudyErpFisheriesCtaHeading, + caseStudyErpFisheriesCtaLabel, + caseStudyErpFisheriesCtaSubject, caseStudyErpFisheriesDesc, caseStudyErpFisheriesTitle, + caseStudySaasVolunteeringPlatformCtaHeading, + caseStudySaasVolunteeringPlatformCtaLabel, + caseStudySaasVolunteeringPlatformCtaSubject, + caseStudySaasVolunteeringPlatformDesc, + caseStudySaasVolunteeringPlatformTitle, ctoaasBestFor, ctoaasDescription, ctoaasHeroDesc, @@ -161,12 +170,6 @@ import { erpWhyUsReasons0, erpWhyUsReasons1, erpWhyUsReasons2, - mainCaseStudies0Category, - mainCaseStudies0Text, - mainCaseStudies0Title, - mainCaseStudies1Category, - mainCaseStudies1Text, - mainCaseStudies1Title, mainFaq0Answer, mainFaq0Question, mainFaq1Answer, @@ -276,23 +279,91 @@ import { tddWhyUsReasons2 } from "@/locales/.generated/strings"; -export function getCaseStudies(lang: SupportedLanguage) { +export enum ServiceSlug { + InferenceAI = "inference-ai", + ErpImplementation = "erp-implementation", + CtoAsAService = "cto-as-a-service", + SoftwareAsAService = "software-as-a-service", + TechDueDiligence = "tech-due-diligence" +} + +export type CaseStudy = { + cta: { + heading: string; + subject: string; + label: string; + }; + description: string; + serviceSlug: ServiceSlug; + slug: string; + title: string; + url: string; +}; + +export enum CaseStudySlug { + Fisheries = "erp-fisheries", + Media = "ctoaas-media", + VolunteeringPlatform = "saas-volunteering-platform" +} + +export function getCaseStudies(lang: SupportedLanguage): CaseStudy[] { + const BASE_PATH = `/${lang}/case-studies`; return [ { - title: mainCaseStudies0Title(lang), - category: mainCaseStudies0Category(lang), - description: mainCaseStudies0Text(lang), - urlCaseStudy: `/${lang}/case-studies/erp-fisheries` + cta: { + heading: caseStudyErpFisheriesCtaHeading(lang), + subject: caseStudyErpFisheriesCtaSubject(lang), + label: caseStudyErpFisheriesCtaLabel(lang) + }, + description: caseStudyErpFisheriesDesc(lang), + serviceSlug: ServiceSlug.CtoAsAService, + slug: CaseStudySlug.Fisheries, + title: caseStudyErpFisheriesTitle(lang), + url: `${BASE_PATH}/${CaseStudySlug.Fisheries}` + }, + { + cta: { + heading: caseStudyCtoaasMediaCtaHeading(lang), + subject: caseStudyCtoaasMediaCtaSubject(lang), + label: caseStudyCtoaasMediaCtaLabel(lang) + }, + description: caseStudyCtoaasMediaDesc(lang), + serviceSlug: ServiceSlug.CtoAsAService, + slug: CaseStudySlug.Media, + title: caseStudyCtoaasMediaTitle(lang), + url: `${BASE_PATH}/${CaseStudySlug.Media}` }, { - title: mainCaseStudies1Title(lang), - category: mainCaseStudies1Category(lang), - description: mainCaseStudies1Text(lang), - urlCaseStudy: `/${lang}/case-studies/ctoaas-media` + cta: { + heading: caseStudySaasVolunteeringPlatformCtaHeading(lang), + subject: caseStudySaasVolunteeringPlatformCtaSubject(lang), + label: caseStudySaasVolunteeringPlatformCtaLabel(lang) + }, + description: caseStudySaasVolunteeringPlatformDesc(lang), + serviceSlug: ServiceSlug.SoftwareAsAService, + slug: CaseStudySlug.VolunteeringPlatform, + title: caseStudySaasVolunteeringPlatformTitle(lang), + url: `${BASE_PATH}/${CaseStudySlug.VolunteeringPlatform}` } ]; } +type CaseStudyByParams = { + lang: SupportedLanguage; + slug: string; +}; + +export function caseStudyBy({ lang, slug }: CaseStudyByParams) { + return getCaseStudies(lang).find((cs) => cs.slug === slug); +} + +function caseStudyByService({ + lang, + slug +}: ServiceBySlugParameters): CaseStudy[] { + return getCaseStudies(lang).filter((cs) => cs.serviceSlug === slug); +} + export function getFaqs(lang: SupportedLanguage) { return [ { @@ -386,22 +457,6 @@ type Content = { }[]; }; -export enum ServiceSlug { - InferenceAI = "inference-ai", - ErpImplementation = "erp-implementation", - CtoAsAService = "cto-as-a-service", - SoftwareAsAService = "software-as-a-service", - TechDueDiligence = "tech-due-diligence" -} - -export type CaseStudy = { - slug: string; - title: string; - description: string; - category: string; - basePath?: string; -}; - export type Service = { bestFor: string[]; caseStudies: CaseStudy[]; @@ -526,7 +581,10 @@ export function services(lang: SupportedLanguage): Service[] { shortDescription: aiHeroDesc(lang), slug: ServiceSlug.InferenceAI, title: aiHeroHeading(lang), - caseStudies: [], + caseStudies: caseStudyByService({ + lang, + slug: ServiceSlug.InferenceAI + }), faqs: [ { question: aiFaq0Question(lang), answer: aiFaq0Answer(lang) }, { question: aiFaq1Question(lang), answer: aiFaq1Answer(lang) }, @@ -787,22 +845,10 @@ export function services(lang: SupportedLanguage): Service[] { shortDescription: ctoaasHeroDesc(lang), slug: ServiceSlug.CtoAsAService, title: ctoaasHeroHeading(lang), - caseStudies: [ - { - slug: "erp-fisheries", - title: caseStudyErpFisheriesTitle(lang), - description: caseStudyErpFisheriesDesc(lang), - category: caseStudyErpFisheriesCategory(lang), - basePath: "case-studies" - }, - { - slug: "ctoaas-media", - title: caseStudyCtoaasMediaTitle(lang), - description: caseStudyCtoaasMediaDesc(lang), - category: caseStudyCtoaasMediaCategory(lang), - basePath: "case-studies" - } - ], + caseStudies: caseStudyByService({ + lang, + slug: ServiceSlug.CtoAsAService + }), faqs: [] }, { @@ -909,7 +955,10 @@ export function services(lang: SupportedLanguage): Service[] { shortDescription: saasHeroDesc(lang), slug: ServiceSlug.SoftwareAsAService, title: saasHeroHeading(lang), - caseStudies: [], + caseStudies: caseStudyByService({ + lang, + slug: ServiceSlug.SoftwareAsAService + }), faqs: [] }, { @@ -1040,7 +1089,10 @@ export function services(lang: SupportedLanguage): Service[] { shortDescription: tddHeroDesc(lang), slug: ServiceSlug.TechDueDiligence, title: tddHeroHeading(lang), - caseStudies: [], + caseStudies: caseStudyByService({ + lang, + slug: ServiceSlug.TechDueDiligence + }), faqs: [] } ]; diff --git a/app/[lang]/(hyperjump)/page.tsx b/app/[lang]/(hyperjump)/page.tsx index d67abb575..e4265d13d 100644 --- a/app/[lang]/(hyperjump)/page.tsx +++ b/app/[lang]/(hyperjump)/page.tsx @@ -1,8 +1,14 @@ +import { ArrowRightIcon } from "lucide-react"; import type { Metadata } from "next"; import Image from "next/image"; import Link from "next/link"; import type { Organization, WebPage, WithContext } from "schema-dts"; +import GridItemsContainer, { + GridItems, + GridItemsMoreButton, + GridItemsTitle +} from "@/app/components/grid-items"; import { Accordion, AccordionContent, @@ -16,12 +22,8 @@ import { CardFooter, CardHeader } from "@/components/ui/card"; -import GridItemsContainer, { - GridItems, - GridItemsMoreButton, - GridItemsTitle -} from "@/app/components/grid-items"; import data from "@/data.json"; +import { dynamicOpengraph } from "@/lib/default-metadata"; import { supportedLanguages, type SupportedLanguage @@ -42,7 +44,7 @@ import { mainCaseStudiesCtaExploreOurCaseStudies, mainHeroHeading, mainFaqLearnMore -} from "@/locales/.generated/server"; +} from "@/locales/.generated/strings"; import { Clients } from "./components/clients"; import { Location } from "./components/location"; @@ -53,8 +55,7 @@ import { pageData, services } from "./data"; -import { dynamicOpengraph } from "@/lib/default-metadata"; -import { ArrowRightIcon } from "lucide-react"; +import { CaseStudyCard } from "./components/case-study-card"; const { github, socials, title, url } = data; @@ -181,12 +182,15 @@ function CaseStudies({ lang }: HomeParams) { title={mainCaseStudiesHeading(lang)} description={mainCaseStudiesDesc(lang)} /> - +
+ {getCaseStudies(lang).map((caseStudy) => ( + + ))} +
- + ); @@ -422,13 +422,15 @@ function WhyUs({ lang, service }: LangProps & ServiceProps) { ); } -type RecommendationProps = { +type CaseStudyProps = { caseStudies: CaseStudy[]; lang: SupportedLanguage; }; -function Recommendation({ caseStudies, lang }: RecommendationProps) { - if (caseStudies.length === 0) return null; +function CaseStudies({ caseStudies, lang }: CaseStudyProps) { + if (caseStudies.length === 0) { + return null; + } return (
@@ -437,33 +439,13 @@ function Recommendation({ caseStudies, lang }: RecommendationProps) { {servicesCaseStudies(lang)}
- {caseStudies.map( - ({ category, description, slug, title, basePath }) => ( -
-
- - {category} - -

- {title} -

-

- {description} -

-
- -
- ) - )} + {caseStudies.map((caseStudy) => ( + + ))}
diff --git a/app/[lang]/(hyperjump)/components/team-card.tsx b/app/[lang]/(hyperjump)/team/card.tsx similarity index 72% rename from app/[lang]/(hyperjump)/components/team-card.tsx rename to app/[lang]/(hyperjump)/team/card.tsx index cb511a654..e2a443993 100644 --- a/app/[lang]/(hyperjump)/components/team-card.tsx +++ b/app/[lang]/(hyperjump)/team/card.tsx @@ -1,26 +1,19 @@ import Image from "next/image"; +import type { Team } from "./data"; -interface TeamCardProps { - variant?: "featured" | "compact"; - name: string; - role: string; - image?: string; - description: string; - linkedIn?: string; -} +type TeamCardProps = { + variant: "featured" | "compact"; +} & Team; const FALLBACK_IMAGE = "/images/no-user-image.webp"; -const CARD_HEIGHT_FEATURED = 680; -const CARD_HEIGHT_COMPACT = 640; - export function TeamCard({ - variant = "compact", + description, + image, + linkedIn, name, role, - image, - description, - linkedIn + variant }: TeamCardProps) { const isFeatured = variant === "featured"; const imageSrc = image && image.trim() !== "" ? image : FALLBACK_IMAGE; @@ -28,10 +21,10 @@ export function TeamCard({ return (
+ className={`flex h-auto flex-col rounded-xl bg-white shadow-sm transition-shadow duration-300 hover:shadow-lg ${isFeatured ? "md:h-170" : "md:h-160"} `}>
@@ -66,7 +60,7 @@ export function TeamCard({ className="mt-4 inline-flex h-9 w-9 items-center justify-center rounded-full border border-[#73767E] text-[#73767E] transition-colors hover:border-[#a1cfff] hover:bg-[#a1cfff]"> LinkedIn diff --git a/app/[lang]/(hyperjump)/team/data.ts b/app/[lang]/(hyperjump)/team/data.ts index b338fc59b..e9252dbb8 100644 --- a/app/[lang]/(hyperjump)/team/data.ts +++ b/app/[lang]/(hyperjump)/team/data.ts @@ -1,148 +1,154 @@ -export function getTeams() { - return [ - { - name: "Ari Awan", - role: "Co-founder & CEO", - image: "1763184214507.jpeg", - description: - "Jack-of-all-trade in technology startups. Experienced in software engineering (20+ years of hands-on web/mobile programming), product management, operation, fundraising, and marketing.", - linkedIn: "https://www.linkedin.com/in/ariawan/" - }, - { - name: "Ariya Hidayat", - role: "Co-founder & CTO", - image: "1689492669383.jpeg", - description: - "Experienced technology leader operating at the intersection of enterprise products and open-source software, with deep hands-on experience across diverse technology stacks.", - linkedIn: "https://www.linkedin.com/in/ariyahidayat/" - }, - { - name: "Satriyo Pranoto", - role: "Head of Product", - image: "1517052947097.jpeg", - description: - "Senior IT and project management professional with 20+ years of experience delivering enterprise and digital products across multiple industries.", - linkedIn: "https://www.linkedin.com/in/satriyo-pranoto-2b1935122/" - }, - { - name: "Zulfahmi Andri", - role: "Principle Software Architect", - image: "1610507885667.jpeg", - description: - "Enterprise software professional with 20+ years of experience, specializing in API-driven systems, mobile applications, and modern cloud architectures.", - linkedIn: "https://www.linkedin.com/in/zulfahmi-andri/" - }, - { - name: "Ferdi Ramdhon", - role: "Head Of Development", - image: "1517464706587.jpeg", - description: - "A full-stack specialist with 10,000+ hours of experience across Java, Python, Go, and React. Expertise spans Android development, SQL/NoSQL databases, and Agile SDLC, focused on delivering scalable software architectures and high-performance RESTful APIs.", - linkedIn: "https://id.linkedin.com/in/ferdirn" - }, - { - name: "Nico Prananta", - role: "Principal Developer", - image: "1636610990869.jpeg", - description: - "Principal software developer with experience leading engineering teams and delivering modern web applications across multiple industries.", - linkedIn: "https://www.linkedin.com/in/nico-prananta-884750200/" - }, - { - name: "Amri S. Pangestu", - role: "Product Manager", - image: "1607277533248.jpeg", - description: - "Product & IT Project Manager with experience in digital agencies, e-grocery, and consulting, driving product growth through strategy, execution, and technology.", - linkedIn: "https://www.linkedin.com/in/amripangestu/" - }, - { - name: "Hari Cahya Nugraha", - role: "Software Engineer", - image: "1606819371215.jpeg", - description: - "Full-stack software engineer with 10+ years of experience building scalable web systems, specializing in backend, infrastructure, and cloud-native architectures.", - linkedIn: "https://www.linkedin.com/in/haricnugraha/" - }, - { - name: "Muslim Ilmiawan", - role: "Software Engineer", - image: "1558886084532.jpeg", - description: - "Backend-focused programmer passionate about system architecture, working mainly with Java, Go, and Node.js.", - linkedIn: "https://www.linkedin.com/in/muslimilmiawan/" - }, - { - name: "Denny Pradipta", - role: "Software Engineer", - image: "1758856751506.jpeg", - description: - "Experienced full-stack web developer with 8+ years of experience working across frontend, backend, and DevOps with modern JavaScript technologies.", - linkedIn: "https://www.linkedin.com/in/dennypradipta/" - }, - { - name: "Raosan Lillahi", - role: "Software Engineer", - image: "1679995906768.jpeg", - description: - "Software engineer with extensive full-stack experience, startup founding background, and strong interest in technology and business.", - linkedIn: "https://www.linkedin.com/in/raosanfikri/" - }, - { - name: "Suyono", - role: "Senior Software Engineer", - image: "1516414668719.jpeg", - description: - "Senior software engineer with 13+ years of experience, passionate about open-source, scalable architectures, and building high-quality products.", - linkedIn: "https://www.linkedin.com/in/suyono/" - }, - { - name: "Kevin Hermawan", - role: "Software Engineer", - image: "1760315021833.jpeg", - description: - "Experienced software engineer focused on frontend, system reliability, and AI-driven solutions, with hands-on work in LLMs, RAG, and monitoring tools.", - linkedIn: "https://www.linkedin.com/in/khermawan/" - }, - { - name: "Budhi Widagdo", - role: "Software Engineer", - image: "1602615800537.jpeg", - description: - "Experienced software engineer with a background in open-source development, digital banking platforms, and leading multi-project engineering teams.", - linkedIn: "https://www.linkedin.com/in/budhi-widagdo/" - }, - { - name: "Sinta Herena", - role: "Software Engineer", - image: "1765007375648.jpeg", - description: - "Full-stack JavaScript developer experienced in React, Next.js, and TypeScript, focused on building reliable, user-centric web applications.", - linkedIn: "https://www.linkedin.com/in/sinta-herena/" - }, - { - name: "Lukman Adisaputro", - role: "Software Engineer", - image: "1635918474641.jpeg", - description: - "Software developer with experience across startups and media companies, specializing in Go backend systems and modern web development with React.", - linkedIn: "https://www.linkedin.com/in/lukman-adisaputro-4a8969214/" - }, - { - name: "M Noor Syam", - role: "Software Engineer", - image: "1750253962765.jpeg", - description: - "Software engineer with 10+ years of experience in Android and full-stack web development, delivering MVPs and scalable, production-ready systems.", - linkedIn: "https://www.linkedin.com/in/mochamad-noor-syamsu-832617a5/" - }, - { - name: "Harianto", - role: "Head of Business Development", - image: "1699844064993.jpeg", - description: - "Business development executive driving long-term growth through strategy, partnerships, and commercial execution.", - linkedIn: "https://www.linkedin.com/in/h-harianto-7493799b/" - } - ]; -} +export type Team = { + name: string; + role: string; + image: string; + description: string; + linkedIn: string; +}; + +export const team: Team[] = [ + { + name: "Ari Awan", + role: "Co-founder & CEO", + image: "1763184214507.jpeg", + description: + "Jack-of-all-trade in technology startups. Experienced in software engineering (20+ years of hands-on web/mobile programming), product management, operation, fundraising, and marketing.", + linkedIn: "https://www.linkedin.com/in/ariawan/" + }, + { + name: "Ariya Hidayat", + role: "Co-founder & CTO", + image: "1689492669383.jpeg", + description: + "Experienced technology leader operating at the intersection of enterprise products and open-source software, with deep hands-on experience across diverse technology stacks.", + linkedIn: "https://www.linkedin.com/in/ariyahidayat/" + }, + { + name: "Satriyo Pranoto", + role: "Head of Product", + image: "1517052947097.jpeg", + description: + "Senior IT and project management professional with 20+ years of experience delivering enterprise and digital products across multiple industries.", + linkedIn: "https://www.linkedin.com/in/satriyo-pranoto-2b1935122/" + }, + { + name: "Zulfahmi Andri", + role: "Principle Software Architect", + image: "1610507885667.jpeg", + description: + "Enterprise software professional with 20+ years of experience, specializing in API-driven systems, mobile applications, and modern cloud architectures.", + linkedIn: "https://www.linkedin.com/in/zulfahmi-andri/" + }, + { + name: "Ferdi Ramdhon", + role: "Head Of Development", + image: "1517464706587.jpeg", + description: + "A full-stack specialist with 10,000+ hours of experience across Java, Python, Go, and React. Expertise spans Android development, SQL/NoSQL databases, and Agile SDLC, focused on delivering scalable software architectures and high-performance RESTful APIs.", + linkedIn: "https://id.linkedin.com/in/ferdirn" + }, + { + name: "Nico Prananta", + role: "Principal Developer", + image: "1636610990869.jpeg", + description: + "Principal software developer with experience leading engineering teams and delivering modern web applications across multiple industries.", + linkedIn: "https://www.linkedin.com/in/nico-prananta-884750200/" + }, + { + name: "Amri S. Pangestu", + role: "Product Manager", + image: "1607277533248.jpeg", + description: + "Product & IT Project Manager with experience in digital agencies, e-grocery, and consulting, driving product growth through strategy, execution, and technology.", + linkedIn: "https://www.linkedin.com/in/amripangestu/" + }, + { + name: "Hari Cahya Nugraha", + role: "Software Engineer", + image: "1606819371215.jpeg", + description: + "Full-stack software engineer with 10+ years of experience building scalable web systems, specializing in backend, infrastructure, and cloud-native architectures.", + linkedIn: "https://www.linkedin.com/in/haricnugraha/" + }, + { + name: "Muslim Ilmiawan", + role: "Software Engineer", + image: "1558886084532.jpeg", + description: + "Backend-focused programmer passionate about system architecture, working mainly with Java, Go, and Node.js.", + linkedIn: "https://www.linkedin.com/in/muslimilmiawan/" + }, + { + name: "Denny Pradipta", + role: "Software Engineer", + image: "1758856751506.jpeg", + description: + "Experienced full-stack web developer with 8+ years of experience working across frontend, backend, and DevOps with modern JavaScript technologies.", + linkedIn: "https://www.linkedin.com/in/dennypradipta/" + }, + { + name: "Raosan Lillahi", + role: "Software Engineer", + image: "1679995906768.jpeg", + description: + "Software engineer with extensive full-stack experience, startup founding background, and strong interest in technology and business.", + linkedIn: "https://www.linkedin.com/in/raosanfikri/" + }, + { + name: "Suyono", + role: "Senior Software Engineer", + image: "1516414668719.jpeg", + description: + "Senior software engineer with 13+ years of experience, passionate about open-source, scalable architectures, and building high-quality products.", + linkedIn: "https://www.linkedin.com/in/suyono/" + }, + { + name: "Kevin Hermawan", + role: "Software Engineer", + image: "1760315021833.jpeg", + description: + "Experienced software engineer focused on frontend, system reliability, and AI-driven solutions, with hands-on work in LLMs, RAG, and monitoring tools.", + linkedIn: "https://www.linkedin.com/in/khermawan/" + }, + { + name: "Budhi Widagdo", + role: "Software Engineer", + image: "1602615800537.jpeg", + description: + "Experienced software engineer with a background in open-source development, digital banking platforms, and leading multi-project engineering teams.", + linkedIn: "https://www.linkedin.com/in/budhi-widagdo/" + }, + { + name: "Sinta Herena", + role: "Software Engineer", + image: "1765007375648.jpeg", + description: + "Full-stack JavaScript developer experienced in React, Next.js, and TypeScript, focused on building reliable, user-centric web applications.", + linkedIn: "https://www.linkedin.com/in/sinta-herena/" + }, + { + name: "Lukman Adisaputro", + role: "Software Engineer", + image: "1635918474641.jpeg", + description: + "Software developer with experience across startups and media companies, specializing in Go backend systems and modern web development with React.", + linkedIn: "https://www.linkedin.com/in/lukman-adisaputro-4a8969214/" + }, + { + name: "M Noor Syam", + role: "Software Engineer", + image: "1750253962765.jpeg", + description: + "Software engineer with 10+ years of experience in Android and full-stack web development, delivering MVPs and scalable, production-ready systems.", + linkedIn: "https://www.linkedin.com/in/mochamad-noor-syamsu-832617a5/" + }, + { + name: "Harianto", + role: "Head of Business Development", + image: "1699844064993.jpeg", + description: + "Business development executive driving long-term growth through strategy, partnerships, and commercial execution.", + linkedIn: "https://www.linkedin.com/in/h-harianto-7493799b/" + } +]; diff --git a/app/[lang]/(hyperjump)/team/page.tsx b/app/[lang]/(hyperjump)/team/page.tsx index 34678c4b0..7d7177cdc 100644 --- a/app/[lang]/(hyperjump)/team/page.tsx +++ b/app/[lang]/(hyperjump)/team/page.tsx @@ -1,38 +1,32 @@ -import type { Metadata } from "next"; import data from "@/data.json"; import { dynamicOpengraph } from "@/lib/default-metadata"; import { supportedLanguages, type SupportedLanguage } from "@/locales/.generated/types"; -import { TeamCard } from "../components/team-card"; -import { getTeams } from "./data"; import { mainTeamDesc, mainTeamHeading } from "@/locales/.generated/strings"; -export const generateStaticParams = async () => { - return supportedLanguages.map((lang) => ({ lang })); -}; -type TeamsProps = { - params: Promise<{ lang: SupportedLanguage }>; -}; +import { TeamCard } from "./card"; +import { team } from "./data"; export async function generateMetadata() { - const { title, description } = data; - - const meta: Metadata = { - title: `Meet Our Team – ${title}`, - description: description - }; + return dynamicOpengraph({ + title: `Meet Our Team - ${data.title}` + }); +} - return dynamicOpengraph(meta); +export async function generateStaticParams() { + return supportedLanguages.map((lang) => ({ lang })); } +type TeamsProps = { + params: Promise<{ lang: SupportedLanguage }>; +}; + export default async function TeamSection({ params }: TeamsProps) { const { lang } = await params; - const teams = getTeams(); - - const featuredTeams = teams.slice(0, 2); - const otherTeams = teams.slice(2); + const founders = team.slice(0, 2); + const members = team.slice(2); return (
@@ -47,15 +41,17 @@ export default async function TeamSection({ params }: TeamsProps) {
- {featuredTeams.map((member, i) => ( - + {founders.map((founder) => ( + ))}
- {otherTeams.map((member, i) => ( - - ))} + {members + .sort((a, b) => a.name.localeCompare(b.name)) + .map((member) => ( + + ))}
diff --git a/app/components/grid-items.tsx b/app/components/grid-items.tsx index e32ac2edb..193c214df 100644 --- a/app/components/grid-items.tsx +++ b/app/components/grid-items.tsx @@ -17,11 +17,7 @@ import { motion } from "framer-motion"; import React, { Children, isValidElement, useEffect, useState } from "react"; import { GitFork, Star } from "lucide-react"; import type { SupportedLanguage } from "@/locales/.generated/types"; -import { - mainCaseStudiesButton, - mainSeeMore, - productsLearnMore -} from "@/locales/.generated/server"; +import { mainSeeMore, productsLearnMore } from "@/locales/.generated/strings"; type GridItemsTitleProps = { id?: string; @@ -82,11 +78,9 @@ export type Item = { title: string; description: string; url?: string; - category?: string; icon?: string | React.ReactElement; button?: boolean; repo?: string; - urlCaseStudy?: string; urlSeeMore?: string; urlLearnMore?: string; repoUrl?: string; @@ -100,25 +94,19 @@ type Column = { xl?: number; }; +type GridItemsProps = { + items: Item[]; + columns?: Column; + cardClassName?: string; + lang: SupportedLanguage; +}; + export function GridItems({ items, columns = { base: 1, md: 2, xl: 3 }, - withCard = true, cardClassName = "border-[#D9D9D9] bg-white", - borderClassName = "", - categoryClassName = "bg-hyperjump-black/10 text-hyperjump-black", - titleClassName = "text-hyperjump-black", lang -}: { - items: Item[]; - cardClassName?: string; - columns?: Column; - withCard?: boolean; - borderClassName?: string; - categoryClassName?: string; - titleClassName?: string; - lang: SupportedLanguage; -}) { +}: GridItemsProps) { const [repoStats, setRepoStats] = useState< Record >({}); @@ -164,10 +152,6 @@ export function GridItems({ fetchRepoStats(); }, [items]); - const CardWrapper = withCard - ? Card - : ({ children }: { children: React.ReactNode }) =>
{children}
; - return (
{items.map((item, idx) => { @@ -176,10 +160,8 @@ export function GridItems({ title, description, url, - category, icon, button, - urlCaseStudy, urlSeeMore, urlLearnMore, repoUrl @@ -188,12 +170,11 @@ export function GridItems({ const isReactIcon = isValidElement(icon); const isStringIcon = typeof icon === "string"; return ( - {image && (
@@ -218,31 +199,14 @@ export function GridItems({ )} - {category && ( -

- {category} -

- )} {url ? ( - + {title} ) : ( - + {title} )} @@ -255,16 +219,6 @@ export function GridItems({ __html: description }} /> - - {urlCaseStudy && ( - - )} - {urlSeeMore && (
diff --git a/app/llms.txt/route.ts b/app/llms.txt/route.ts index b76f7ade9..c05b71c14 100644 --- a/app/llms.txt/route.ts +++ b/app/llms.txt/route.ts @@ -1,6 +1,6 @@ import { NextResponse } from "next/server"; import data from "@/data.json"; -import { mainOpenInGoogleMaps } from "@/locales/.generated/server"; +import { mainOpenInGoogleMaps } from "@/locales/.generated/strings"; import { getCommercialProduct, getOpenSource @@ -85,7 +85,7 @@ function serviceList() { function caseStudyList() { return getCaseStudies(LOCALE) .map( - ({ description, title, urlCaseStudy }) => + ({ description, title, url: urlCaseStudy }) => `- [${title}](${url}${urlCaseStudy}): ${description}` ) .join("\n"); diff --git a/e2e/case-study-detail.spec.ts b/e2e/case-study-detail.spec.ts index 80f5f68b9..1438449b1 100644 --- a/e2e/case-study-detail.spec.ts +++ b/e2e/case-study-detail.spec.ts @@ -1,6 +1,6 @@ import { test, expect, Page } from "@playwright/test"; -import { getCaseStudies } from "@/app/[lang]/(hyperjump)/case-studies/data"; import { imagesTest } from "./shared-test"; +import { getCaseStudies } from "@/app/[lang]/(hyperjump)/data"; // Base URL const baseURL = "http://localhost:3000"; diff --git a/e2e/homepage.spec.ts b/e2e/homepage.spec.ts index 4b1a73f4d..a0da6b97c 100644 --- a/e2e/homepage.spec.ts +++ b/e2e/homepage.spec.ts @@ -92,7 +92,9 @@ test.describe("Homepage", () => { for (const s of services) { await expect(page.getByText(s.desc)).toBeVisible(); await expect(page.getByRole("img", { name: s.title })).toBeVisible(); - await expect(page.getByText(s.title, { exact: true })).toBeVisible(); + await expect( + page.locator("#services").getByText(s.title, { exact: true }) + ).toBeVisible(); } // View more link diff --git a/locales/en/case-studies/fisheries.mdx b/locales/en/case-studies/fisheries.mdx index 19a0b490d..ca6384238 100644 --- a/locales/en/case-studies/fisheries.mdx +++ b/locales/en/case-studies/fisheries.mdx @@ -4,7 +4,7 @@ import Image from "next/image"; A growing fisheries startup faced a familiar challenge: a passionate but inexperienced tech team, inexperienced leadership, and a critical mission. Digitize Indonesia's fisheries supply chain from coast to customer. The stakes were high, but the potential impact was massive. -With no live product and a team just finding its feet, the company partnered with Hyperjump under a CTO-as-a-Service model to build strong technical foundations, processes, and culture. +With no live product and a team just finding its feet, the company partnered with Hyperjump under a **[CTO-as-a-Service](https://hyperjump.tech/id/services/cto-as-a-service)** model to build strong technical foundations, processes, and culture. ## The Challenge @@ -45,7 +45,7 @@ Working closely over 12 months, Hyperjump embedded with the product, engineering - QA isn't a department—it's a shared cultural commitment - Documentation is your secret weapon. A growing internal wiki and consistent Jira practices made onboarding and scaling smoother -## What’s Next +## What's Next The team is now stable and self-sufficient. The next challenge is to strengthen internal leadership and establish a long-term product roadmap to keep delivering value at scale. We're proud to have helped this fisheries company build not just software, but a high-performing tech organization that's ready to transform the industry. diff --git a/locales/en/case-studies/volunteering-platform.mdx b/locales/en/case-studies/volunteering-platform.mdx index fda27b8e5..c8c1978d9 100644 --- a/locales/en/case-studies/volunteering-platform.mdx +++ b/locales/en/case-studies/volunteering-platform.mdx @@ -2,7 +2,7 @@ import Image from "next/image"; ## Background -Indonesia’s largest volunteering platform decided to update its outdated technology by migrating from Angular.js to React. As a nonprofit organization with limited budget, they found managing an in-house engineering team costly and difficult. +Indonesia's largest volunteering platform decided to update its outdated technology by migrating from Angular.js to React. As a nonprofit organization with limited budget, they found managing an in-house engineering team costly and difficult. Hyperjump is proud to support this project as part of our corporate social responsibility (CSR) program, providing technology expertise to help drive social impact. @@ -15,7 +15,7 @@ Hyperjump is proud to support this project as part of our corporate social respo ## Our Approach -Hyperjump worked closely with the client’s product, engineering, and operations teams. Here’s what we accomplished: +Hyperjump worked closely with the client's product, engineering, and operations teams. Here's what we accomplished: ### Incremental Migration & Modernization @@ -49,7 +49,7 @@ After months of close collaboration, we achieved the following: