feat(wip): nearly done

This commit is contained in:
Reinaldy Rafli 2021-07-15 16:39:59 +07:00
parent 34d9be965e
commit c9f9452227
19 changed files with 730 additions and 522 deletions

View File

@ -9,6 +9,7 @@
"url": "https://github.com/aldy505"
}
],
"type": "module",
"scripts": {
"dev": "svelte-kit dev",
"build": "svelte-kit build",
@ -19,29 +20,31 @@
"format": "prettier --write --ignore-path .gitignore --plugin-search-dir=. \"./**/*.(ts|json|js|svelte)\""
},
"devDependencies": {
"@sveltejs/adapter-static": "^1.0.0-next.13",
"@sveltejs/kit": "next",
"@types/cookie": "^0.4.0",
"@typescript-eslint/eslint-plugin": "^4.19.0",
"@typescript-eslint/parser": "^4.19.0",
"cssnano": "^5.0.6",
"eslint": "^7.22.0",
"eslint-config-prettier": "^8.1.0",
"eslint-plugin-svelte3": "^3.2.0",
"prettier": "~2.2.1",
"prettier-plugin-svelte": "^2.2.0",
"svelte": "^3.34.0",
"svelte-check": "^2.0.0",
"svelte-preprocess": "^4.7.3",
"svelte-windicss-preprocess": "^4.0.12",
"tailwindcss": "^2.2.4",
"tslib": "^2.0.0",
"typescript": "^4.0.0"
"@sveltejs/adapter-static": "1.0.0-next.13",
"@sveltejs/kit": "1.0.0-next.129",
"@types/cookie": "0.4.1",
"@typescript-eslint/eslint-plugin": "4.28.3",
"@typescript-eslint/parser": "4.28.3",
"cssnano": "5.0.6",
"eslint": "7.30.0",
"eslint-config-prettier": "8.3.0",
"eslint-plugin-svelte3": "3.2.0",
"prettier": "2.3.2",
"prettier-plugin-svelte": "2.3.1",
"svelte": "3.38.3",
"svelte-check": "2.2.2",
"svelte-preprocess": "4.7.4",
"svelte-windicss-preprocess": "4.0.12",
"tslib": "2.3.0",
"typescript": "4.3.5"
},
"type": "module",
"dependencies": {
"@fontsource/fira-mono": "^4.2.2",
"@lukeed/uuid": "^2.0.0",
"cookie": "^0.4.1"
"@fontsource/fira-mono": "4.5.0",
"@fontsource/rubik": "4.5.0",
"@lukeed/uuid": "2.0.0",
"@sentry/browser": "^6.9.0",
"cookie": "0.4.1",
"dotenv": "10.0.0",
"svelte-i18n": "3.3.9"
}
}

View File

@ -0,0 +1,3 @@
<div class="bg-gray-900 font-code px-4 md:px-8 py-6 rounded-lg text-white overflow-ellipsis whitespace-nowrap overflow-x-scroll md:overflow-auto shadow-xl bg-gradient-to-br from-transparent to-gray-800">
<slot></slot>
</div>

View File

@ -1,3 +1,18 @@
<nav class="flex flex-col md:flex-row">
<div class="flex-1">Home</div>
<script lang="ts">
import { goto } from "$app/navigation";
import { _ } from "svelte-i18n";
// TODO: Create hamburger navigation (I know, just bit lazy on this one)
</script>
<nav class="flex flex-col md:flex-row py-6 font-body">
<div on:click={() => goto('/')} class="hover:cursor-pointer flex-5 font-bold text-2xl">Jokesbapak2</div>
<div on:click={() => goto('/why')} class="navigation-item">{$_('navigation.why')}</div>
<div on:click={() => goto('/guide')} class="navigation-item">{$_('navigation.guide')}</div>
<div on:click={() => goto('/api')} class="navigation-item">{$_('navigation.api')}</div>
</nav>
<style>
.navigation-item {
@apply flex-1 duration-300 transition ease-in-out py-2 'md:py-0' 'md:opacity-50' 'hover:cursor-pointer' 'hover:scale-110' 'hover:opacity-100';
}
</style>

View File

@ -0,0 +1,87 @@
{
"meta": {
"title": "Jokesbapak2",
"tagline": "Largest collection of Indonesian dad jokes",
"explanation": "Instead of returning a joke in JSON format like others normally would, we return it on images!"
},
"error": {
"heading": "Whoops, you got an error!",
"homepage": "Click here to go to homepage."
},
"navigation": {
"why": "Why?",
"guide": "Guide",
"api": "API"
},
"footer": {
"made": "Made with",
"indonesia": "in Indonesia",
"available": "This project is available on",
"github": "Github"
},
"home": {
"more": {
"1": "Find out more on",
"2": "and"
}
},
"why": {
"exists": {
"title": "Why does this project exist?",
"body": {
"1": "Sometime around early 2020, I was interested on seeing Indonesian dad jokes all over Twitter.",
"2": "By the end of the month, I was obsessed with it.",
"3": "Then, it was that time where I learn and created my Discord bot,",
"4": "which I added a feature which you can ask for Indonesian dad jokes on a form of an image.",
"5": "I thought, why not make it into an API where people can use it too?",
"6": "Therefore, this was born."
}
},
"submit": {
"title": "Can I submit my dad joke?",
"body": {
"1": "You sure can!",
"2": "Submit the image to",
"3": "my email",
"4": ", and I'll get back to you as soon as possible.",
"5": "Bear in mind, your joke (or at least the content of the image) must not insult anyone and be racists.",
"6": "Let's keep the jokes healthy."
}
},
"contribute": {
"title": "I don't have any dad joke, can I contribute?",
"body": {
"1": "Of course!",
"2": "This project is",
"3": "open sourced on Github.",
"4": "Feel free to crunch a bug, add a feature, or fix a small typo."
}
},
"inquiries": {
"title": "I have other inquiries",
"body": {
"1": "You can contact me through",
"2": "my email",
"3": "and I'll be in touch as soon as possible."
}
}
},
"api": {
"get": {
"title": "Get Jokes",
"random": {
"title": "Get single random joke",
"body": "You'll get different result for every call."
},
"today": {
"title": "Get today's joke",
"body": "A joke a day makes more of a dad out of you."
},
"id": {
"title": "Get joke by ID",
"body": "You'll get consistent joke for every call with the same ID."
}
},
"more": "More documentation will soon arrive here..."
}
}

View File

@ -0,0 +1,87 @@
{
"meta": {
"title": "Jokesbapak2",
"tagline": "Koleksi jokes bapak2 terbesar",
"explanation": "Daripada mengirimkan candaan dalam format JSON seperti yang lainnya, kami mengirimkan gambar!"
},
"error": {
"heading": "Ups, Bapak dapat error!",
"homepage": "Klik disini untuk kembali ke halaman awal."
},
"navigation": {
"why": "Mengapa?",
"guide": "Panduan",
"api": "API"
},
"footer": {
"made": "Diciptakan dengan",
"indonesia": "di Indonesia",
"available": "Proyek ini tersedia di",
"github": "Github"
},
"home": {
"more": {
"1": "Cari tahu lebih lanjut di",
"2": "dan"
}
},
"why": {
"exists": {
"title": "Why does this project exist?",
"body": {
"1": "Sometime around early 2020, I was interested on seeing Indonesian dad jokes all over Twitter.",
"2": "By the end of the month, I was obsessed with it.",
"3": "Then, it was that time where I learn and created my Discord bot,",
"4": "which I added a feature which you can ask for Indonesian dad jokes on a form of an image.",
"5": "I thought, why not make it into an API where people can use it too?",
"6": "Therefore, this was born."
}
},
"submit": {
"title": "Can I submit my dad joke?",
"body": {
"1": "You sure can!",
"2": "Submit the image to",
"3": "my email",
"4": ", and I'll get back to you as soon as possible.",
"5": "Bear in mind, your joke (or at least the content of the image) must not insult anyone and be racists.",
"6": "Let's keep the jokes healthy."
}
},
"contribute": {
"title": "I don't have any dad joke, can I contribute?",
"body": {
"1": "Of course!",
"2": "This project is",
"3": "open sourced on Github.",
"4": "Feel free to crunch a bug, add a feature, or fix a small typo."
}
},
"inquiries": {
"title": "I have other inquiries",
"body": {
"1": "You can contact me through",
"2": "my email",
"3": "and I'll be in touch as soon as possible."
}
}
},
"api": {
"get": {
"title": "Dapatkan Jokes",
"random": {
"title": "Dapatkan joke acak",
"body": "Bapak akan dapat hasil yang berbeda pada setiap request."
},
"today": {
"title": "Dapatkan joke hari ini",
"body": "Satu joke akan membuat Bapak semakin Bapak2."
},
"id": {
"title": "Dapatkan joke berdasarkan ID",
"body": "Bapak akan dapat hasil yang konsisten pada setiap request dengan ID yang sama."
}
},
"more": "Dokumentasi lebih lanjut akan segera tiba disini..."
}
}

4
client/src/lib/env.ts Normal file
View File

@ -0,0 +1,4 @@
export default {
API_ENDPOINT: import.meta.env.VITE_API_ENDPOINT,
SENTRY_DSN: import.meta.env.VITE_SENTRY_DSN
}

15
client/src/lib/locale.ts Normal file
View File

@ -0,0 +1,15 @@
import { init, getLocaleFromNavigator, addMessages, getLocaleFromQueryString } from 'svelte-i18n';
import en from '../languages/en.json'
import id from '../languages/id.json'
addMessages('en', en)
addMessages('en-US', en)
addMessages('en-GB', en)
addMessages('id', id)
addMessages('id-ID', id)
init({
fallbackLocale: 'en',
initialLocale: getLocaleFromQueryString('lang') || getLocaleFromNavigator(),
});

View File

@ -0,0 +1,9 @@
import * as Sentry from "@sentry/browser";
import env from "./env";
Sentry.init({
dsn: String(env.SENTRY_DSN),
tracesSampleRate: 0.5,
});
export default Sentry

View File

@ -1,7 +0,0 @@
/**
* TODO: Check user locale, then determines whether they should go to english route or indonesian route.
*/
const getLanguage = () => navigator?.languages[0] || navigator?.language || 'en';
export {}

View File

@ -0,0 +1,18 @@
<script lang="ts" context="module">
import type { LoadOutput } from '@sveltejs/kit';
import Sentry from '$lib/logging';
export async function load({ error }: LoadOutput): Promise<void> {
Sentry.captureException(error)
}
</script>
<script lang="ts">
import { goto } from '$app/navigation'
import { _ } from 'svelte-i18n'
</script>
<section>
<h1 class="text-5xl font-bold">{$_('error.heading')}</h1>
<p class="text-base" on:click={() => goto('/')}>{$_('error.homepage')}</p>
</section>

View File

@ -1,46 +1,32 @@
<script lang="ts" context="module">
import '$lib/locale'
</script>
<script lang="ts">
//import '../locale'
import '@fontsource/fira-mono'
import '@fontsource/rubik'
import { _ } from 'svelte-i18n';
import Navbar from '../components/navbar.svelte';
</script>
<div class="bg-gradient-to-br from-transparent to-lavender-300 dark:(bg-gray-900 to-lavender-900 text-white) min-h-screen h-full w-full">
<div class="container mx-auto lg:px-40 md:20 sm:px-12 px-8">
<header>
<Navbar />
</header>
<main>
<main class="font-body">
<slot />
</main>
<footer>
<p>visit <a href="https://kit.svelte.dev">kit.svelte.dev</a> to learn SvelteKit</p>
<footer class="font-body py-8">
<p class="text-sm opacity-50 hover:opacity-90 transition duration-300 ease-in-out">
{$_('footer.made')} <span class="text-red-500">&#10084;</span> {$_('footer.indonesia')}.
{$_('footer.available')} <a href="https://www.github.com/aldy505/jokes-bapak2-api" class="hover:underline">{$_('footer.github')}</a>.
</p>
</footer>
</div>
</div>
<style windi:preflights:global windi:safelist:global>
main {
flex: 1;
display: flex;
flex-direction: column;
padding: 1rem;
width: 100%;
max-width: 1024px;
margin: 0 auto;
box-sizing: border-box;
}
footer {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 40px;
}
footer a {
font-weight: bold;
}
@media (min-width: 480px) {
footer {
padding: 40px 0;
}
}
</style>
<slot />

View File

@ -1,50 +0,0 @@
<script context="module">
import { browser, dev } from '$app/env';
// we don't need any JS on this page, though we'll load
// it in dev so that we get hot module replacement...
export const hydrate = dev;
// ...but if the client-side router is already loaded
// (i.e. we came here from elsewhere in the app), use it
export const router = browser;
// since there's no dynamic data here, we can prerender
// it so that it gets served as a static asset in prod
export const prerender = true;
</script>
<svelte:head>
<title>About</title>
</svelte:head>
<div class="content">
<h1>About this app</h1>
<p>
This is a <a href="https://kit.svelte.dev">SvelteKit</a> app. You can make your own by typing the following into your
command line and following the prompts:
</p>
<!-- TODO lose the @next! -->
<pre>npm init svelte@next</pre>
<p>
The page you're looking at is purely static HTML, with no client-side interactivity needed. Because of that, we
don't need to load any JavaScript. Try viewing the page's source, or opening the devtools network panel and
reloading.
</p>
<p>
The <a href="/todos">TODOs</a> page illustrates SvelteKit's data loading and form handling. Try using it with JavaScript
disabled!
</p>
</div>
<style>
.content {
width: 100%;
max-width: var(--column-width);
margin: var(--column-margin-top) auto 0 auto;
}
</style>

View File

@ -1,4 +1,45 @@
<script lang="ts">
// TODO: Move to specific locale
// This page is meant to explain available API endpoints.
import { _ } from 'svelte-i18n'
import env from '$lib/env'
import Codeblock from '../components/codeblock.svelte'
</script>
<svelte:head>
<title>{$_('navigation.api')} - {$_('meta.title')}</title>
</svelte:head>
<section>
<h1>{$_('api.get.title')}</h1>
<h2>{$_('api.get.random.title')}</h2>
<p>{$_('api.get.random.body')}</p>
<Codeblock>
GET {env.API_ENDPOINT}/
</Codeblock>
<h2>{$_('api.get.today.title')}</h2>
<p>{$_('api.get.today.body')}</p>
<Codeblock>
GET {env.API_ENDPOINT}/today
</Codeblock>
<h2>{$_('api.get.id.title')}</h2>
<p>{$_('api.get.id.body')}</p>
<Codeblock>
GET {env.API_ENDPOINT}/&lcub;id&rcub;
</Codeblock>
</section>
<section>
<p class="pt-8">{$_('api.more')}</p>
</section>
<style>
h1 {
@apply text-4xl font-bold py-4;
}
h2 {
@apply text-2xl font-bold pt-6 pb-1;
}
p {
@apply text-base opacity-80 py-2;
}
</style>

View File

@ -1,3 +1,12 @@
<script lang="ts">
// TODO: Move to specific locale, turns the page for guide (per language if I could)
import { _ } from 'svelte-i18n'
// This page is meant to guide people on how to use the API.
</script>
<svelte:head>
<title>{$_('navigation.guide')} - {$_('meta.title')}</title>
</svelte:head>
<section>
<p class="text-lg py-4">Sorry for the inconvinience. For now, please refer to the <a class="underline" href="/api">API</a> first. I'll work on this page later on.</p>
</section>

View File

@ -1,16 +1,36 @@
<script context="module" lang="ts">
export const prerender = true;
</script>
<script lang="ts">
import { _ } from 'svelte-i18n'
import { goto } from '$app/navigation'
import env from '$lib/env';
import Codeblock from '../components/codeblock.svelte';
</script>
<svelte:head>
<title>Home</title>
<title>{$_('meta.title')} - {$_('meta.tagline')}</title>
</svelte:head>
<section class="">
<section>
<div class="flex flex-col md:flex-row items-center py-8">
<div class="flex-1">
<h1 class="text-6xl font-bold py-2">{$_('meta.tagline')}</h1>
<p class="text-base py-4 w-2/3">{$_('meta.explanation')}</p>
</div>
<div class="flex-1 px-6">
<div class="max-w-xs mx-auto">
<img src={env.API_ENDPOINT + `/today`} alt="Sample joke" class="py-6 shadow-2xl">
</div>
<Codeblock class="px-4 md:px-0 mx-auto">
$ curl -XGET 'https://jokesbapak2.herokuapp.com/v1/'
</Codeblock>
<p class="text-sm text-center py-4 opacity-70 hover:opacity-100 transition duration-300 ease-in-out">
{$_('home.more.1')} <span on:click={() => goto('/guide')}>{$_('navigation.guide')}</span> {$_('home.more.2')} <span on:click={() => goto('/api')}>{$_('navigation.api')}</span>
</p>
</div>
</div>
</section>
<style>
span {
@apply 'hover:underline' cursor-pointer;
}
</style>

View File

@ -0,0 +1,43 @@
<script lang="ts">
import { _ } from 'svelte-i18n'
</script>
<svelte:head>
<title>{$_('navigation.why')} - {$_('meta.title')}</title>
</svelte:head>
<section>
<h1 id="why-does-this-project-exists">{$_('why.exists.title')}</h1>
<p>{$_('why.exists.body.1')} {$_('why.exists.body.2')} {$_('why.exists.body.3')} {$_('why.exists.body.4')}</p>
<p>{$_('why.exists.body.5')} {$_('why.exists.body.6')}</p>
<h1 id="can-i-submit-my-dad-joke">{$_('why.submit.title')}</h1>
<p>
{$_('why.submit.body.1')} {$_('why.submit.body.2')}
<a href="mailto:aldy505@tutanota.com">{$_('why.submit.body.3')}</a>
{$_('why.submit.body.4')} {$_('why.submit.body.5')} {$_('why.submit.body.6')}
</p>
<h1 id="can-i-contribute">{$_('why.contribute.title')}</h1>
<p>
{$_('why.contribute.body.1')} {$_('why.contribute.body.2')}
<a href="https://www.github.com/aldy505/jokes-bapak2-api">{$_('why.contribute.body.3')}</a>
{$_('why.contribute.body.4')}
</p>
<h1 id="other-inquiries">{$_('why.inquiries.title')}</h1>
<p>
{$_('why.inquiries.body.1')}
<a href="mailto:aldy505@tutanota.com">{$_('why.inquiries.body.2')}</a>, {$_('why.inquiries.body.3')}
</p>
</section>
<style>
p {
@apply text-base py-2 'md:w-2/3';
}
h1 {
@apply text-4xl font-bold py-4;
}
a {
/* This would probably be an error if you installed WindiCSS extension on VSCode */
@apply 'hover:underline' 'dark:text-dodger-200' text-dodger-700;
}
</style>

View File

@ -6,13 +6,24 @@ import { windi } from 'svelte-windicss-preprocess';
const config = {
// Consult https://github.com/sveltejs/svelte-preprocess
// for more information about preprocessors
preprocess: [windi(), preprocess({ postcss: false })],
preprocess: [
windi({
configPath: './windi.config.js',
}),
preprocess({ postcss: false })
],
kit: {
// hydrate the <div id="svelte"> element in src/app.html
target: '#svelte',
ssr: false,
trailingSlash: 'never',
files: {
routes: './src/routes',
assets: './static',
hooks: './src',
lib: './src/lib'
},
adapter: adapter({
// default options are shown
pages: 'dist',

57
client/windi.config.js Normal file
View File

@ -0,0 +1,57 @@
import { defineConfig } from 'windicss/helpers'
export default defineConfig({
darkMode: 'media',
theme: {
extend: {
fontFamily: {
code: ['"Fira Mono"', '"Source Code Pro"', '"Lucida Console"', '"Courier New"', 'Courier', 'sans-serif'],
body: ['Rubik', 'Asap', 'Barlow', 'Arial', 'sans-serif'],
},
flex: {
2: '2 2 0%',
3: '3 3 0%',
4: '4 4 0%',
5: '5 5 0%',
},
colors: {
chetwode: {
50: '#f9fafe',
100: '#f3f4fe',
200: '#e1e4fc',
300: '#cfd4f9',
400: '#abb4f5',
500: '#8794f1',
600: '#7a85d9',
700: '#656fb5',
800: '#515991',
900: '#424976',
},
dodger: {
50: '#f4f9fe',
100: '#e8f2fd',
200: '#c6dffa',
300: '#a4cbf7',
400: '#5fa5f2',
500: '#1b7eec',
600: '#1871d4',
700: '#145fb1',
800: '#104c8e',
900: '#0d3e74',
},
lavender: {
50: '#fefbff',
100: '#fef6fe',
200: '#fceafd',
300: '#fbddfb',
400: '#f7c3f8',
500: '#f4a9f5',
600: '#dc98dd',
700: '#b77fb8',
800: '#926593',
900: '#785378',
}
}
}
}
})

File diff suppressed because it is too large Load Diff