Skip to content

Commit fc34ccd

Browse files
committed
project cover image and description added
1 parent 21f7ee7 commit fc34ccd

16 files changed

Lines changed: 270 additions & 97 deletions

File tree

site/src/jsMain/kotlin/org/kotlang/portfolio/components/layout/ProjectShowcaseLayout.kt

Lines changed: 14 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -2,36 +2,30 @@ package org.kotlang.portfolio.components.layout
22

33
import androidx.compose.runtime.Composable
44
import androidx.compose.runtime.LaunchedEffect
5-
import com.varabyte.kobweb.compose.css.FontWeight
6-
import com.varabyte.kobweb.compose.css.TextAlign
75
import com.varabyte.kobweb.compose.foundation.layout.Column
86
import com.varabyte.kobweb.compose.ui.Alignment
97
import com.varabyte.kobweb.compose.ui.Modifier
10-
import com.varabyte.kobweb.compose.ui.modifiers.*
11-
import com.varabyte.kobweb.silk.components.graphics.Image
12-
import com.varabyte.kobweb.silk.components.text.SpanText
13-
import com.varabyte.kobweb.silk.style.toModifier
14-
import org.jetbrains.compose.web.css.percent
15-
import org.jetbrains.compose.web.css.px
8+
import com.varabyte.kobweb.compose.ui.modifiers.fillMaxWidth
9+
import org.kotlang.portfolio.components.sections.DesktopScreenshotSection
1610
import org.kotlang.portfolio.components.sections.ScreenshotSection
17-
import org.kotlang.portfolio.components.sections.SectionTitle
11+
import org.kotlang.portfolio.components.sections.ShowcaseHeroSection
1812
import org.kotlang.portfolio.components.sections.TechStackSection
1913
import org.kotlang.portfolio.models.TechStack
2014
import org.kotlang.portfolio.sections.ProjectPageHeader
21-
import org.kotlang.portfolio.theme.SectionContainerStyle
2215
import org.kotlang.portfolio.util.setPageMetadata
2316

2417
@Composable
2518
fun ProjectShowcaseLayout(
19+
coverImage: String,
2620
pageTitle: String,
2721
pageDescription: String,
28-
screenshots: List<String>? = null,
22+
screenshots: List<String>,
2923
techStack: List<TechStack>,
3024
desktopScreenshot: String? = null,
31-
bannerImageLink: String? = null,
3225
githubLink: String? = null,
3326
playStoreLink: String? = null,
34-
courseLink: String? = null
27+
courseLink: String? = null,
28+
descriptionContent: @Composable () -> Unit
3529
) {
3630
LaunchedEffect(Unit) {
3731
setPageMetadata(
@@ -53,45 +47,16 @@ fun ProjectShowcaseLayout(
5347
modifier = Modifier.fillMaxWidth(),
5448
horizontalAlignment = Alignment.CenterHorizontally
5549
) {
56-
Column(
57-
modifier = SectionContainerStyle.toModifier().gap(16.px),
58-
horizontalAlignment = Alignment.CenterHorizontally
59-
) {
60-
Image(
61-
src = bannerImageLink
62-
?: "https://github.com/kotlang-dev/AgeCalculator-TrackDates/blob/main/.github/assets/banner.png?raw=true",
63-
alt = "Age Calculator Banner",
64-
modifier = Modifier.fillMaxWidth().borderRadius(12.px)
65-
)
66-
SpanText(
67-
text = pageTitle,
68-
modifier = Modifier.fontSize(32.px).fontWeight(FontWeight.Bold).textAlign(TextAlign.Center)
69-
)
70-
SpanText(
71-
text = pageDescription,
72-
modifier = Modifier.textAlign(TextAlign.Center).opacity(80.percent)
73-
)
74-
}
50+
ShowcaseHeroSection(
51+
title = pageTitle,
52+
coverImage = coverImage,
53+
description = descriptionContent
54+
)
7555

76-
screenshots?.let {
77-
ScreenshotSection(screenshots = it)
78-
}
56+
ScreenshotSection(screenshots = screenshots)
7957

8058
if (desktopScreenshot != null) {
81-
Column(
82-
modifier = SectionContainerStyle.toModifier().padding(top = 0.px),
83-
horizontalAlignment = Alignment.CenterHorizontally
84-
) {
85-
SectionTitle(
86-
title = "Desktop Preview",
87-
subtitle = "See it in Action"
88-
)
89-
Image(
90-
src = desktopScreenshot,
91-
alt = "Desktop Screenshot",
92-
modifier = Modifier.fillMaxWidth(90.percent)
93-
)
94-
}
59+
DesktopScreenshotSection(desktopScreenshot)
9560
}
9661

9762
TechStackSection(techStack = techStack)
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package org.kotlang.portfolio.components.sections
2+
3+
import androidx.compose.runtime.Composable
4+
import com.varabyte.kobweb.compose.foundation.layout.Column
5+
import com.varabyte.kobweb.compose.ui.Alignment
6+
import com.varabyte.kobweb.compose.ui.Modifier
7+
import com.varabyte.kobweb.compose.ui.modifiers.fillMaxWidth
8+
import com.varabyte.kobweb.compose.ui.modifiers.padding
9+
import com.varabyte.kobweb.silk.components.graphics.Image
10+
import com.varabyte.kobweb.silk.style.toModifier
11+
import org.jetbrains.compose.web.css.percent
12+
import org.jetbrains.compose.web.css.px
13+
import org.kotlang.portfolio.theme.SectionContainerStyle
14+
15+
@Composable
16+
fun DesktopScreenshotSection(
17+
desktopScreenshot: String
18+
) {
19+
Column(
20+
modifier = SectionContainerStyle.toModifier().padding(top = 0.px),
21+
horizontalAlignment = Alignment.CenterHorizontally
22+
) {
23+
SectionTitle(
24+
title = "Desktop Preview",
25+
subtitle = "See it in Action"
26+
)
27+
Image(
28+
src = desktopScreenshot,
29+
alt = "Desktop Screenshot",
30+
modifier = Modifier.fillMaxWidth(90.percent)
31+
)
32+
}
33+
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package org.kotlang.portfolio.components.sections
2+
3+
import androidx.compose.runtime.Composable
4+
import com.varabyte.kobweb.compose.foundation.layout.Box
5+
import com.varabyte.kobweb.compose.foundation.layout.Column
6+
import com.varabyte.kobweb.compose.foundation.layout.Row
7+
import com.varabyte.kobweb.compose.ui.Alignment
8+
import com.varabyte.kobweb.compose.ui.Modifier
9+
import com.varabyte.kobweb.compose.ui.modifiers.fillMaxWidth
10+
import com.varabyte.kobweb.compose.ui.modifiers.gap
11+
import com.varabyte.kobweb.compose.ui.modifiers.padding
12+
import com.varabyte.kobweb.framework.annotations.DelicateApi
13+
import com.varabyte.kobweb.silk.components.graphics.Image
14+
import com.varabyte.kobweb.silk.components.text.SpanText
15+
import com.varabyte.kobweb.silk.style.breakpoint.Breakpoint
16+
import com.varabyte.kobweb.silk.style.toModifier
17+
import com.varabyte.kobweb.silk.theme.breakpoint.rememberBreakpoint
18+
import org.jetbrains.compose.web.css.percent
19+
import org.jetbrains.compose.web.css.px
20+
import org.kotlang.portfolio.theme.ShowcaseImageContainerStyle
21+
import org.kotlang.portfolio.theme.ShowcaseImageStyle
22+
import org.kotlang.portfolio.theme.ShowcaseTitleStyle
23+
24+
@OptIn(DelicateApi::class)
25+
@Composable
26+
fun ShowcaseHeroSection(
27+
title: String,
28+
description: @Composable () -> Unit,
29+
coverImage: String
30+
) {
31+
val breakpoint = rememberBreakpoint()
32+
33+
Box(
34+
modifier = Modifier
35+
.fillMaxWidth()
36+
.padding(
37+
bottom = 60.px,
38+
left = if (breakpoint >= Breakpoint.MD) 60.px else 0.px
39+
),
40+
contentAlignment = Alignment.Center
41+
) {
42+
if (breakpoint >= Breakpoint.MD) {
43+
Row(
44+
modifier = Modifier.gap(20.px),
45+
verticalAlignment = Alignment.CenterVertically
46+
) {
47+
Intro(
48+
modifier = Modifier.fillMaxWidth(40.percent).gap(10.px),
49+
title = title,
50+
description = description
51+
)
52+
CoverImage(coverImage = coverImage)
53+
}
54+
} else {
55+
Column(
56+
modifier = Modifier.fillMaxWidth().gap(20.px),
57+
horizontalAlignment = Alignment.CenterHorizontally
58+
) {
59+
CoverImage(coverImage = coverImage)
60+
Intro(
61+
modifier = Modifier.gap(10.px).padding(leftRight = 10.px),
62+
title = title,
63+
description = description
64+
)
65+
}
66+
}
67+
}
68+
}
69+
70+
@Composable
71+
private fun Intro(
72+
modifier: Modifier = Modifier,
73+
title: String,
74+
description: @Composable () -> Unit
75+
) {
76+
Column(modifier = modifier) {
77+
SpanText(title, modifier = ShowcaseTitleStyle.toModifier())
78+
description()
79+
}
80+
}
81+
82+
@Composable
83+
private fun CoverImage(
84+
coverImage: String
85+
) {
86+
Box(
87+
modifier = ShowcaseImageContainerStyle.toModifier()
88+
) {
89+
Image(
90+
src = coverImage,
91+
alt = "Cover Image",
92+
modifier = ShowcaseImageStyle.toModifier()
93+
)
94+
}
95+
}

site/src/jsMain/kotlin/org/kotlang/portfolio/models/PortfolioItem.kt

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,6 @@ data class PortfolioItem(
1212

1313
object PortfolioData {
1414
val projects = listOf(
15-
PortfolioItem(
16-
icon = Res.Image.APP_ICON_AGE_CALC,
17-
title = "Age Calculator (KMP)",
18-
description = "A multiplatform app for Android & Desktop with a shared codebase using Compose.",
19-
link = Routes.PROJECTS_AGE_CALCULATOR,
20-
tags = listOf("Compose Multiplatform", "Kotlinx DateTime", "Room DB", "Koin (DI)", "Pref DataStore")
21-
),
2215
PortfolioItem(
2316
icon = Res.Image.APP_ICON_KINEX,
2417
title = "Kinex - Gym Workouts",
@@ -27,11 +20,11 @@ object PortfolioData {
2720
tags = listOf("Ktor", "Jetpack Compose", "Coroutines", "SQLDelight")
2821
),
2922
PortfolioItem(
30-
icon = Res.Image.APP_ICON_QUIZTIME,
31-
title = "Whiteboard",
32-
description = "A fitness tracking app built with Jetpack Compose and Firebase.",
33-
link = Routes.PROJECTS_WHITEBOARD,
34-
tags = listOf("Firebase", "Jetpack Compose", "Dagger Hilt", "MVVM")
23+
icon = Res.Image.APP_ICON_AGE_CALC,
24+
title = "Age Calculator (KMP)",
25+
description = "A multiplatform app for Android & Desktop with a shared codebase using Compose.",
26+
link = Routes.PROJECTS_AGE_CALCULATOR,
27+
tags = listOf("Compose Multiplatform", "Kotlinx DateTime", "Room DB", "Koin (DI)", "Pref DataStore")
3528
)
3629
)
3730

site/src/jsMain/kotlin/org/kotlang/portfolio/models/Routes.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
package org.kotlang.portfolio.models
22

33
object Routes {
4-
const val PROJECTS_AGE_CALCULATOR = "/projects/age-calc"
54
const val PROJECTS_KINEX = "/projects/kinex"
6-
const val PROJECTS_WHITEBOARD = "/projects/whiteboard"
5+
const val PROJECTS_AGE_CALCULATOR = "/projects/age-calc"
76

87
const val COURSES_QUIZ_TIME = "/courses/quiz-time"
98
const val COURSES_MEASURE_MATE = "/courses/measure-mate"

site/src/jsMain/kotlin/org/kotlang/portfolio/pages/courses/MeasureMatePage.kt

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,23 @@ package org.kotlang.portfolio.pages.courses
22

33
import androidx.compose.runtime.Composable
44
import com.varabyte.kobweb.core.Page
5+
import com.varabyte.kobweb.silk.components.text.SpanText
6+
import com.varabyte.kobweb.silk.style.toModifier
57
import org.kotlang.portfolio.components.layout.ProjectShowcaseLayout
68
import org.kotlang.portfolio.models.Routes
79
import org.kotlang.portfolio.models.TechStack
10+
import org.kotlang.portfolio.theme.ShowcaseDescriptionStyle
811
import org.kotlang.portfolio.util.Res
912

1013
@Page(Routes.COURSES_MEASURE_MATE)
1114
@Composable
1215
fun MeasureMatePage() {
1316
ProjectShowcaseLayout(
1417
pageTitle = "MeasureMate Android App",
15-
pageDescription = "A comprehensive fitness tracking app built with Jetpack Compose and Firebase, created as part of a premium Udemy course.",
18+
pageDescription = "A comprehensive fitness tracking app built with Jetpack Compose and Firebase",
1619
githubLink = Res.Link.MEASUREMATE_GITHUB,
1720
courseLink = Res.Link.MEASUREMATE_COURSE,
21+
coverImage = "/courses/measure_mate/cover.jpeg",
1822
screenshots = listOf(
1923
"/courses/measure_mate/screenshot_01.png",
2024
"/courses/measure_mate/screenshot_02.png",
@@ -29,6 +33,16 @@ fun MeasureMatePage() {
2933
TechStack.Firebase,
3034
TechStack.DaggerHilt,
3135
TechStack.ComposeNavigation
32-
)
36+
),
37+
descriptionContent = {
38+
SpanText(
39+
text = "A fitness tracking app built with Jetpack Compose and Material 3, featuring a modern, declarative UI.",
40+
modifier = ShowcaseDescriptionStyle.toModifier()
41+
)
42+
SpanText(
43+
text = "Powered by Firebase for authentication and real-time Firestore data, the app ensures secure sign-ins and smooth syncing. Dagger Hilt is used for dependency management.",
44+
modifier = ShowcaseDescriptionStyle.toModifier()
45+
)
46+
}
3347
)
3448
}

site/src/jsMain/kotlin/org/kotlang/portfolio/pages/courses/QuizTimePage.kt

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,23 @@ package org.kotlang.portfolio.pages.courses
22

33
import androidx.compose.runtime.Composable
44
import com.varabyte.kobweb.core.Page
5+
import com.varabyte.kobweb.silk.components.text.SpanText
6+
import com.varabyte.kobweb.silk.style.toModifier
57
import org.kotlang.portfolio.components.layout.ProjectShowcaseLayout
68
import org.kotlang.portfolio.models.Routes
79
import org.kotlang.portfolio.models.TechStack
10+
import org.kotlang.portfolio.theme.ShowcaseDescriptionStyle
811
import org.kotlang.portfolio.util.Res
912

1013
@Page(Routes.COURSES_QUIZ_TIME)
1114
@Composable
1215
fun QuizTimePage() {
1316
ProjectShowcaseLayout(
14-
pageTitle = "QuizTime Android App",
17+
pageTitle = "QuizTime: A Full-Stack Android App",
1518
pageDescription = "A full-stack quiz application featuring a Ktor backend and a Jetpack Compose UI for Android, developed as part of a premium Udemy course.",
1619
githubLink = Res.Link.QUIZTIME_GITHUB,
1720
courseLink = Res.Link.QUIZTIME_COURSE,
21+
coverImage = "/courses/quiz_time/cover.jpg",
1822
screenshots = listOf(
1923
"/courses/quiz_time/screenshot_01.png",
2024
"/courses/quiz_time/screenshot_02.png",
@@ -29,6 +33,16 @@ fun QuizTimePage() {
2933
TechStack.Koin,
3034
TechStack.Ktor,
3135
TechStack.MongoDb
32-
)
36+
),
37+
descriptionContent = {
38+
SpanText(
39+
text = "A full-stack quiz application built entirely with Kotlin. The Android client uses Jetpack Compose with MVVM and Clean Architecture, featuring smooth UI, state management, and offline caching.",
40+
modifier = ShowcaseDescriptionStyle.toModifier()
41+
)
42+
SpanText(
43+
text = "On the backend, it offers both Ktor and Spring Boot implementations serving REST APIs with MongoDB for data storage.",
44+
modifier = ShowcaseDescriptionStyle.toModifier()
45+
)
46+
}
3347
)
3448
}

site/src/jsMain/kotlin/org/kotlang/portfolio/pages/projects/AgeCalculatorPage.kt

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,23 @@ package org.kotlang.portfolio.pages.projects
22

33
import androidx.compose.runtime.Composable
44
import com.varabyte.kobweb.core.Page
5+
import com.varabyte.kobweb.silk.components.text.SpanText
6+
import com.varabyte.kobweb.silk.style.toModifier
57
import org.kotlang.portfolio.components.layout.ProjectShowcaseLayout
68
import org.kotlang.portfolio.models.Routes
79
import org.kotlang.portfolio.models.TechStack
10+
import org.kotlang.portfolio.theme.ShowcaseDescriptionStyle
811
import org.kotlang.portfolio.util.Res
912

1013
@Page(Routes.PROJECTS_AGE_CALCULATOR)
1114
@Composable
1215
fun AgeCalculatorPage() {
1316
ProjectShowcaseLayout(
1417
pageTitle = "Age Calculator - Track Dates (KMP)",
15-
pageDescription = "A modern, offline-first app designed to track the age of important life events, built with Kotlin Multiplatform.",
18+
pageDescription = "A modern, offline-first app designed to track the age of important life events.",
1619
githubLink = Res.Link.AGE_CALC_GITHUB,
1720
playStoreLink = Res.Link.AGE_CALC_PLAYSTORE,
21+
coverImage = "/projects/age_calculator/cover.jpeg",
1822
screenshots = listOf(
1923
"/projects/age_calculator/screenshot_01.png",
2024
"/projects/age_calculator/screenshot_02.png",
@@ -31,6 +35,16 @@ fun AgeCalculatorPage() {
3135
TechStack.PreferenceDataStore,
3236
TechStack.KotlinxDateTime,
3337
TechStack.Koin
34-
)
38+
),
39+
descriptionContent = {
40+
SpanText(
41+
text = "A modern, offline-first app designed to track the age of important life events. This project showcases modern, multi-platform development best practices with a clean, shared architecture and a reactive, adaptive UI.",
42+
modifier = ShowcaseDescriptionStyle.toModifier()
43+
)
44+
SpanText(
45+
text = "It also features a fully automated CI/CD pipeline for desktop releases—all built with Kotlin Multiplatform and Compose Multiplatform.",
46+
modifier = ShowcaseDescriptionStyle.toModifier()
47+
)
48+
}
3549
)
3650
}

0 commit comments

Comments
 (0)