1 ---
2 import IndexLayout from '@/layouts/IndexLayout.astro'
3 import { SITE } from '@/config'
4 import { Image } from 'astro:assets'
5 ---
6
7 <IndexLayout
8 title={`Unduh - ${SITE.title}`}
9 description="Hubungi tim PBJ Komplit untuk pertanyaan, saran, atau feedback"
10 >
11 <div class="contact-page prose">
12 <h1>Unduh Aplikasi PBJ Komplit Bahasa Jawa</h1>
13
14 <p>
15 Aplikasi PBJ Komplit Bahasa Jawa tersedia dalam berbagai platform, mulai dari iOS, Android,
16 macOS, Windows, dan Linux.
17 </p>
18
19 <div class="contact-info">
20 <h2>Unduh</h2>
21 <a href="https://apps.apple.com/se/app/pepak-basa-jawa-komplit/id6752241812">
22 <Image
23 src="/images/Download_on_the_App_Store_Badge_US-UK_RGB_blk_092917.svg"
24 alt="PBJ Komplit Apple AppStore page"
25 width="150"
26 height="50"
27 />
28 </a>
29 <a href="https://play.google.com/store/apps/details?id=com.theblangkon.pepakbasajawa">
30 <Image
31 src="/images/GetItOnGooglePlay_Badge_Web_color_English.png"
32 alt="PBJ Komplit Google PlayStore page"
33 width="150"
34 height="50"
35 />
36 </a>
37 <a
38 href="https://apps.microsoft.com/detail/xp8cdrldjdsdph?ocid=webpdpshare"
39 target="_blank"
40 rel="noopener"
41 aria-label="Get PBJ Komplit Basa Jawa from the Microsoft Store"
42 >
43 <img
44 src="https://get.microsoft.com/images/en-us%20dark.svg"
45 alt="Get PBJ Komplit Basa Jawa from Microsoft Store"
46 style="height:50px;width:auto;display:block;margin:0.5rem 0;"
47 width="200"
48 />
49 </a>
50
51 <h2>Desktop Apps</h2>
52 <div id="desktop-downloads">
53 <p>Loading desktop app downloads...</p>
54 </div>
55
56 <h2>Bantuan</h2>
57 <p>Butuh bantuan? Hubungi kami:</p>
58 <div class="email-section">
59 <a href="mailto:splitfire@setoelkahfi.se" class="email-link"> splitfire@setoelkahfi.se </a>
60 </div>
61 </div>
62 </div>
63 </IndexLayout>
64
65 <script>
66 interface DownloadLink {
67 platform: string
68 architecture?: string
69 url: string
70 filename: string
71 size: number
72 version: string
73 }
74
75 interface DownloadsResponse {
76 version: string
77 name: string
78 published_at: string
79 downloads: DownloadLink[]
80 }
81
82 function formatBytes(bytes: number): string {
83 if (bytes === 0) return '0 Bytes'
84 const k = 1024
85 const sizes = ['Bytes', 'KB', 'MB', 'GB']
86 const i = Math.floor(Math.log(bytes) / Math.log(k))
87 return Math.round((bytes / Math.pow(k, i)) * 100) / 100 + ' ' + sizes[i]
88 }
89
90 function getPlatformIcon(platform: string): string {
91 switch (platform.toLowerCase()) {
92 case 'macos':
93 return '🍎'
94 case 'windows':
95 return '🪟'
96 case 'linux':
97 return '🐧'
98 default:
99 return '💻'
100 }
101 }
102
103 async function loadDesktopDownloads() {
104 const container = document.getElementById('desktop-downloads')
105 if (!container) return
106
107 try {
108 const response = await fetch('/api/download-desktop-app')
109 if (!response.ok) {
110 throw new Error('Failed to fetch downloads')
111 }
112
113 const data: DownloadsResponse = await response.json()
114
115 if (!data.downloads || data.downloads.length === 0) {
116 container.innerHTML = '<p>No desktop downloads available at the moment.</p>'
117 return
118 }
119
120 // Group downloads by platform
121 const groupedDownloads = data.downloads.reduce(
122 (acc, download) => {
123 if (!acc[download.platform]) {
124 acc[download.platform] = []
125 }
126 acc[download.platform].push(download)
127 return acc
128 },
129 {} as Record<string, DownloadLink[]>
130 )
131
132 let html = `<div class="version-info">Latest Version: ${data.version}</div>`
133 html += '<div class="downloads-grid">'
134
135 // Sort platforms: macOS, Windows, Linux
136 const platformOrder = ['macOS', 'Windows', 'Linux']
137 const sortedPlatforms = Object.keys(groupedDownloads).sort(
138 (a, b) => platformOrder.indexOf(a) - platformOrder.indexOf(b)
139 )
140
141 for (const platform of sortedPlatforms) {
142 const downloads = groupedDownloads[platform]
143 html += `
144 <div class="platform-section">
145 <h3>${getPlatformIcon(platform)} ${platform}</h3>
146 <div class="download-items">
147 `
148
149 for (const download of downloads) {
150 html += `
151 <a href="${download.url}" class="download-item">
152 <div class="download-info">
153 <span class="download-name">${download.architecture || 'Download'}</span>
154 <span class="download-details">${download.filename} • ${formatBytes(download.size)}</span>
155 </div>
156 <span class="download-button">⬇️ Download</span>
157 </a>
158 `
159 }
160
161 html += `
162 </div>
163 </div>
164 `
165 }
166
167 html += '</div>'
168 container.innerHTML = html
169 } catch (error) {
170 console.error('Error loading desktop downloads:', error)
171 container.innerHTML =
172 '<p class="error-message">Unable to load desktop downloads. Please try again later.</p>'
173 }
174 }
175
176 // Load downloads when page is ready
177 if (document.readyState === 'loading') {
178 document.addEventListener('DOMContentLoaded', loadDesktopDownloads)
179 } else {
180 loadDesktopDownloads()
181 }
182 </script>
183
184 <style>
185 .contact-page {
186 max-width: 100%;
187 margin: 0 auto;
188 padding: 2rem 0;
189 }
190
191 .contact-info {
192 margin: 2rem 0;
193 }
194
195 .email-section {
196 margin: 1.5rem 0;
197 padding: 1rem;
198 background: var(--code-bg);
199 border-radius: 8px;
200 border-left: 4px solid var(--accent);
201 }
202
203 .email-link {
204 font-size: 1.25rem;
205 font-weight: 600;
206 color: var(--accent);
207 text-decoration: none;
208 word-break: break-all;
209 }
210
211 .email-link:hover {
212 text-decoration: underline;
213 }
214
215 .topics {
216 margin: 2rem 0;
217 }
218
219 .topics ul {
220 list-style-type: disc;
221 margin-left: 1.5rem;
222 }
223
224 .topics li {
225 margin: 0.5rem 0;
226 line-height: 1.6;
227 }
228
229 .app-info,
230 .community {
231 margin: 2rem 0;
232 }
233
234 .app-info a {
235 color: var(--accent);
236 text-decoration: none;
237 }
238
239 .app-info a:hover {
240 text-decoration: underline;
241 }
242
243 .footer-note {
244 margin: 3rem 0 1rem;
245 padding: 1.5rem;
246 background: var(--selection);
247 border-radius: 8px;
248 text-align: center;
249 }
250
251 .footer-note p {
252 margin: 0.5rem 0;
253 }
254
255 #desktop-downloads {
256 margin: 1.5rem 0;
257 }
258
259 .version-info {
260 padding: 0.75rem 1rem;
261 background: var(--selection);
262 border-radius: 6px;
263 margin-bottom: 1.5rem;
264 font-weight: 600;
265 color: var(--accent);
266 }
267
268 .downloads-grid {
269 display: grid;
270 gap: 2rem;
271 margin-top: 1.5rem;
272 }
273
274 .platform-section {
275 border: 1px solid var(--border);
276 border-radius: 8px;
277 padding: 1.5rem;
278 background: var(--code-bg);
279 }
280
281 .platform-section h3 {
282 margin: 0 0 1rem 0;
283 font-size: 1.5rem;
284 color: var(--heading-color);
285 }
286
287 .download-items {
288 display: flex;
289 flex-direction: column;
290 gap: 0.75rem;
291 }
292
293 .download-item {
294 display: flex;
295 justify-content: space-between;
296 align-items: center;
297 padding: 1rem;
298 background: var(--bg);
299 border: 1px solid var(--border);
300 border-radius: 6px;
301 text-decoration: none;
302 transition: all 0.2s ease;
303 }
304
305 .download-item:hover {
306 border-color: var(--accent);
307 transform: translateY(-2px);
308 box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
309 }
310
311 .download-info {
312 display: flex;
313 flex-direction: column;
314 gap: 0.25rem;
315 }
316
317 .download-name {
318 font-weight: 600;
319 color: var(--heading-color);
320 font-size: 1.1rem;
321 }
322
323 .download-details {
324 font-size: 0.875rem;
325 color: var(--text-muted);
326 }
327
328 .download-button {
329 padding: 0.5rem 1rem;
330 background: var(--accent);
331 color: white;
332 border-radius: 4px;
333 font-weight: 600;
334 white-space: nowrap;
335 }
336
337 .error-message {
338 color: #e74c3c;
339 padding: 1rem;
340 background: rgba(231, 76, 60, 0.1);
341 border-radius: 6px;
342 border-left: 4px solid #e74c3c;
343 }
344
345 @media (max-width: 768px) {
346 .contact-page {
347 padding: 1rem 0;
348 }
349
350 .email-link {
351 font-size: 1.1rem;
352 }
353
354 .download-item {
355 flex-direction: column;
356 align-items: flex-start;
357 gap: 1rem;
358 }
359
360 .download-button {
361 width: 100%;
362 text-align: center;
363 }
364
365 .platform-section {
366 padding: 1rem;
367 }
368 }
369 </style>