├── .all-contributorsrc ├── .editorconfig ├── .eslintrc.json ├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── feature_request.md │ └── submit-ui-design.md ├── .gitignore ├── .husky ├── pre-commit └── pre-push ├── .lintstagedrc.js ├── .prettier.config.js ├── .vscode └── settings.json ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── CONTRIBUTING_DESIGN.md ├── FUNDING.yml ├── LICENSE ├── README.md ├── app ├── (authenticated) │ ├── layout.tsx │ └── me │ │ ├── page.tsx │ │ ├── submission │ │ └── [[...params]] │ │ │ └── page.tsx │ │ └── submissions │ │ └── [[...params]] │ │ └── page.tsx ├── actions.ts ├── auth │ ├── callback │ │ └── route.ts │ └── login │ │ └── page.tsx ├── challenge │ └── [slug] │ │ ├── head.tsx │ │ ├── page.tsx │ │ └── submission │ │ └── page.tsx ├── challenges │ └── page.tsx ├── code-the-design.ico ├── construction │ └── page.tsx ├── favicon.ico ├── globals.css ├── layout.tsx ├── manual │ └── page.tsx ├── page.tsx ├── submissions │ └── page.tsx ├── test-1 │ └── page.tsx ├── test-2 │ └── page.tsx └── test │ └── route.ts ├── commitlint.config.js ├── components ├── Analytics.tsx ├── AuthNav.tsx ├── Box.tsx ├── Button.tsx ├── CategoriesSelector.tsx ├── CategoriesSelectorClient.tsx ├── Challenge.tsx ├── Challenges.tsx ├── Combobox.tsx ├── Designers.tsx ├── DifficultiesSelector.tsx ├── DifficultiesSelectorClient.tsx ├── Dropdown.tsx ├── EmptyState.tsx ├── FigmaPreview.tsx ├── FileUpload.tsx ├── Form.tsx ├── Header.tsx ├── LoadMore.tsx ├── Meta.tsx ├── ScrollToTop.tsx ├── SectionTitle.tsx ├── Select.tsx ├── Sidebar.tsx ├── SidebarNav.tsx ├── Steps.tsx ├── Submission.tsx ├── SubmissionForm.tsx ├── SubmissionModeCheckbox.tsx └── TechSelector.tsx ├── data ├── challenges.ts └── difficulties.ts ├── drizzle.config.ts ├── drizzle ├── 0000_aromatic_mathemanic.sql ├── 0001_icy_aqueduct.sql ├── 0002_black_otto_octavius.sql ├── 0003_happy_jack_power.sql ├── 0004_steep_luckman.sql ├── 0005_white_arachne.sql ├── 0006_luxuriant_electro.sql ├── 0007_curvy_blazing_skull.sql ├── 0008_shiny_nuke.sql ├── 0009_keen_frightful_four.sql ├── 0010_zippy_rick_jones.sql ├── 0011_complete_sabra.sql ├── 0012_sharp_senator_kelly.sql ├── 0013_nifty_ender_wiggin.sql ├── 0014_illegal_machine_man.sql └── meta │ ├── 0000_snapshot.json │ ├── 0001_snapshot.json │ ├── 0002_snapshot.json │ ├── 0003_snapshot.json │ ├── 0004_snapshot.json │ ├── 0005_snapshot.json │ ├── 0006_snapshot.json │ ├── 0007_snapshot.json │ ├── 0008_snapshot.json │ ├── 0009_snapshot.json │ ├── 0010_snapshot.json │ ├── 0011_snapshot.json │ ├── 0012_snapshot.json │ ├── 0013_snapshot.json │ ├── 0014_snapshot.json │ └── _journal.json ├── lib ├── db.ts ├── hooks │ ├── usePageChanges.ts │ └── useSearchParamsManipulator.ts ├── loader-image.ts ├── migrate.ts ├── session.ts ├── storage.ts ├── supabase.ts └── utils.ts ├── middleware.ts ├── middlewares └── auth-middleware.ts ├── next-env.d.ts ├── next.config.js ├── package.json ├── pnpm-lock.yaml ├── postcss.config.js ├── public ├── DumpingDoodle.png ├── alfian.jpeg ├── challenges │ ├── 5 │ │ ├── adoptme.webp │ │ ├── agency.webp │ │ ├── agenone.webp │ │ ├── al-nasr.webp │ │ ├── authed.webp │ │ ├── bertumbuh.webp │ │ ├── bubble-bash.webp │ │ ├── chat-n-rechat.webp │ │ ├── chatflow-landing.webp │ │ ├── chatflow.webp │ │ ├── clone-netflix.webp │ │ ├── collosal.webp │ │ ├── comments.webp │ │ ├── construction.webp │ │ ├── destinize.webp │ │ ├── digidaw.webp │ │ ├── dressly.webp │ │ ├── edufree.webp │ │ ├── enlighten.webp │ │ ├── epictetus.webp │ │ ├── evenity.webp │ │ ├── faceless.webp │ │ ├── grolin.webp │ │ ├── hangeulin.webp │ │ ├── holadok.webp │ │ ├── hoster-support.webp │ │ ├── jobless.webp │ │ ├── kourse.webp │ │ ├── lidia.webp │ │ ├── movies.webp │ │ ├── mstskp.webp │ │ ├── musix-player.webp │ │ ├── nft.webp │ │ ├── nowted-app.webp │ │ ├── nowted.webp │ │ ├── omah.webp │ │ ├── online-learning.webp │ │ ├── priced.webp │ │ ├── pricy.webp │ │ ├── profile-hover.webp │ │ ├── suxz.webp │ │ ├── swiftship.webp │ │ ├── tailor.webp │ │ ├── team-collaboration.webp │ │ ├── testimoni.webp │ │ ├── the-malaka.webp │ │ ├── the-starter.webp │ │ ├── the-sugihartos.webp │ │ ├── the-zeitplan.webp │ │ ├── twitter-embed.webp │ │ └── wumbo.webp │ ├── 1000 │ │ ├── adoptme.webp │ │ ├── agency.webp │ │ ├── agenone.webp │ │ ├── al-nasr.webp │ │ ├── authed.webp │ │ ├── bertumbuh.webp │ │ ├── bubble-bash.webp │ │ ├── chat-n-rechat.webp │ │ ├── chatflow-landing.webp │ │ ├── chatflow.webp │ │ ├── clone-netflix.webp │ │ ├── collosal.webp │ │ ├── comments.webp │ │ ├── construction.webp │ │ ├── destinize.webp │ │ ├── digidaw.webp │ │ ├── dressly.webp │ │ ├── edufree.webp │ │ ├── enlighten.webp │ │ ├── epictetus.webp │ │ ├── evenity.webp │ │ ├── faceless.webp │ │ ├── grolin.webp │ │ ├── hangeulin.webp │ │ ├── holadok.webp │ │ ├── hoster-support.webp │ │ ├── jobless.webp │ │ ├── kourse.webp │ │ ├── lidia.webp │ │ ├── movies.webp │ │ ├── mstskp.webp │ │ ├── musix-player.webp │ │ ├── nft.webp │ │ ├── nowted-app.webp │ │ ├── nowted.webp │ │ ├── omah.webp │ │ ├── online-learning.webp │ │ ├── priced.webp │ │ ├── pricy.webp │ │ ├── profile-hover.webp │ │ ├── suxz.webp │ │ ├── swiftship.webp │ │ ├── tailor.webp │ │ ├── team-collaboration.webp │ │ ├── testimoni.webp │ │ ├── the-malaka.webp │ │ ├── the-starter.webp │ │ ├── the-sugihartos.webp │ │ ├── the-zeitplan.webp │ │ ├── twitter-embed.webp │ │ └── wumbo.webp │ ├── adoptme.webp │ ├── agency.webp │ ├── agenone.webp │ ├── al-nasr.webp │ ├── authed.webp │ ├── bertumbuh.webp │ ├── bubble-bash.webp │ ├── chat-n-rechat.webp │ ├── chatflow-landing.webp │ ├── chatflow.webp │ ├── clone-netflix.webp │ ├── collosal.webp │ ├── comments.webp │ ├── construction.webp │ ├── destinize.webp │ ├── digidaw.webp │ ├── dressly.webp │ ├── edufree.webp │ ├── enlighten.webp │ ├── epictetus.webp │ ├── evenity.webp │ ├── faceless.webp │ ├── grolin.webp │ ├── hangeulin.webp │ ├── holadok.webp │ ├── hoster-support.webp │ ├── jobless.webp │ ├── kourse.webp │ ├── lidia.webp │ ├── movies.webp │ ├── mstskp.webp │ ├── musix-player.webp │ ├── nft.webp │ ├── nowted-app.webp │ ├── nowted.webp │ ├── omah.webp │ ├── online-learning.webp │ ├── priced.webp │ ├── pricy.webp │ ├── profile-hover.webp │ ├── suxz.webp │ ├── swiftship.webp │ ├── tailor.webp │ ├── team-collaboration.webp │ ├── testimoni.webp │ ├── the-malaka.webp │ ├── the-starter.webp │ ├── the-sugihartos.webp │ ├── the-zeitplan.webp │ ├── thumbs │ │ ├── adoptme.webp │ │ ├── agency.webp │ │ ├── agenone.webp │ │ ├── al-nasr.webp │ │ ├── authed.webp │ │ ├── bertumbuh.webp │ │ ├── bubble-bash.webp │ │ ├── chat-n-rechat.webp │ │ ├── chatflow-landing.webp │ │ ├── chatflow.webp │ │ ├── clone-netflix.webp │ │ ├── collosal.webp │ │ ├── comments.webp │ │ ├── construction.webp │ │ ├── destinize.webp │ │ ├── digidaw.webp │ │ ├── dressly.webp │ │ ├── edufree.webp │ │ ├── enlighten.webp │ │ ├── epictetus.webp │ │ ├── evenity.webp │ │ ├── faceless.webp │ │ ├── grolin.webp │ │ ├── hangeulin.webp │ │ ├── holadok.webp │ │ ├── hoster-support.webp │ │ ├── jobless.webp │ │ ├── kourse.webp │ │ ├── lidia.webp │ │ ├── movies.webp │ │ ├── mstskp.webp │ │ ├── musix-player.webp │ │ ├── nft.webp │ │ ├── nowted-app.webp │ │ ├── nowted.webp │ │ ├── omah.webp │ │ ├── online-learning.webp │ │ ├── priced.webp │ │ ├── pricy.webp │ │ ├── profile-hover.webp │ │ ├── suxz.webp │ │ ├── swiftship.webp │ │ ├── tailor.webp │ │ ├── team-collaboration.webp │ │ ├── testimoni.webp │ │ ├── the-malaka.webp │ │ ├── the-starter.webp │ │ ├── the-sugihartos.webp │ │ ├── the-zeitplan.webp │ │ ├── twitter-embed.webp │ │ └── wumbo.webp │ ├── twitter-embed.webp │ └── wumbo.webp ├── code-design.png ├── code-the-design.svg ├── codedesign-bg.png ├── codedesign-logo.png ├── lidia.png ├── nav-sfx-2.mp3 ├── nav-sfx-3.mp3 ├── nav-sfx.mp3 ├── section-header-quests.png └── users │ ├── afin.jpeg │ ├── alfian.jpeg │ ├── arie.jpeg │ ├── brainstew.jpeg │ ├── dimas.jpeg │ ├── dzaki.jpeg │ ├── fauzan.jpeg │ ├── glenn.jpeg │ ├── irham.jpeg │ ├── irvan.jpeg │ ├── mahyu.jpeg │ ├── nauval.jpg │ ├── rafiq.png │ ├── syauqi.jpeg │ ├── taufik.jpeg │ └── yohana.jpeg ├── schemas ├── badges.ts ├── categories.ts ├── challenge_designers.tsx ├── challenges.ts ├── difficulties.ts ├── submission_techs.ts ├── submission_users.ts ├── submissions.ts ├── techs.ts ├── user_badges.ts ├── user_links.ts ├── user_points.ts └── users.ts ├── seeds ├── data │ └── tech-data.ts ├── seed.ts └── tech-seed.ts ├── services ├── auth-service.ts ├── category-service.ts ├── challenge-service.ts ├── difficulty-service.ts ├── point_service.tsx ├── submission-service.ts ├── tech-service.ts └── user-service.ts ├── tailwind.config.js ├── tsconfig.json └── types └── css.d.ts /.all-contributorsrc: -------------------------------------------------------------------------------- 1 | { 2 | "files": [ 3 | "README.md" 4 | ], 5 | "imageSize": 100, 6 | "commit": false, 7 | "commitConvention": "angular", 8 | "contributors": [ 9 | { 10 | "login": "nauvalazhar", 11 | "name": "Nauval", 12 | "avatar_url": "https://avatars.githubusercontent.com/u/14899175?v=4", 13 | "profile": "http://nauv.al", 14 | "contributions": [ 15 | "code", 16 | "design", 17 | "ideas", 18 | "doc" 19 | ] 20 | }, 21 | { 22 | "login": "wiscaksono", 23 | "name": "Wisnu Wicaksono", 24 | "avatar_url": "https://avatars.githubusercontent.com/u/63142229?v=4", 25 | "profile": "https://wiscaksono.me/", 26 | "contributions": [ 27 | "code", 28 | "design" 29 | ] 30 | }, 31 | { 32 | "login": "fiqryq", 33 | "name": "Fiqry choerudin", 34 | "avatar_url": "https://avatars.githubusercontent.com/u/25787603?v=4", 35 | "profile": "http://fiqry.dev", 36 | "contributions": [ 37 | "code" 38 | ] 39 | }, 40 | { 41 | "login": "mrtampan", 42 | "name": "Achmad Rivaldi", 43 | "avatar_url": "https://avatars.githubusercontent.com/u/33930946?v=4", 44 | "profile": "https://mrtampan.github.io/", 45 | "contributions": [ 46 | "code" 47 | ] 48 | }, 49 | { 50 | "login": "perdiDev", 51 | "name": "Perdi", 52 | "avatar_url": "https://avatars.githubusercontent.com/u/108800230?v=4", 53 | "profile": "http://perdidev.github.io", 54 | "contributions": [ 55 | "code", 56 | "design", 57 | "doc" 58 | ] 59 | }, 60 | { 61 | "login": "hendraaagil", 62 | "name": "Hendra Agil", 63 | "avatar_url": "https://avatars.githubusercontent.com/u/54741166?v=4", 64 | "profile": "https://hendraaagil.dev", 65 | "contributions": [ 66 | "code" 67 | ] 68 | }, 69 | { 70 | "login": "up2dul", 71 | "name": "Abdul Malik", 72 | "avatar_url": "https://avatars.githubusercontent.com/u/36098718?v=4", 73 | "profile": "https://up2dul.com", 74 | "contributions": [ 75 | "code" 76 | ] 77 | }, 78 | { 79 | "login": "haikelz", 80 | "name": "ハキム", 81 | "avatar_url": "https://avatars.githubusercontent.com/u/77146709?v=4", 82 | "profile": "https://haikel.my.id", 83 | "contributions": [ 84 | "code" 85 | ] 86 | }, 87 | { 88 | "login": "iamyuu", 89 | "name": "Yusuf", 90 | "avatar_url": "https://avatars.githubusercontent.com/u/45778229?v=4", 91 | "profile": "https://github.com/iamyuu", 92 | "contributions": [ 93 | "code", 94 | "design" 95 | ] 96 | }, 97 | { 98 | "login": "mahdanidn", 99 | "name": "M. Mahdani Rahmatullah", 100 | "avatar_url": "https://avatars.githubusercontent.com/u/50037482?v=4", 101 | "profile": "https://github.com/mahdanidn", 102 | "contributions": [ 103 | "code" 104 | ] 105 | }, 106 | { 107 | "login": "alfanjauhari", 108 | "name": "Alfan Jauhari", 109 | "avatar_url": "https://avatars.githubusercontent.com/u/57592351?v=4", 110 | "profile": "https://alfanjauhari.com", 111 | "contributions": [ 112 | "code" 113 | ] 114 | }, 115 | { 116 | "login": "sglkc", 117 | "name": "Seya", 118 | "avatar_url": "https://avatars.githubusercontent.com/u/31957516?v=4", 119 | "profile": "https://sglkc.my.id", 120 | "contributions": [ 121 | "code" 122 | ] 123 | }, 124 | { 125 | "login": "altafsyah", 126 | "name": "Altaf Syah", 127 | "avatar_url": "https://avatars.githubusercontent.com/u/57579846?v=4", 128 | "profile": "https://github.com/altafsyah", 129 | "contributions": [ 130 | "design" 131 | ] 132 | } 133 | ], 134 | "contributorsPerLine": 7, 135 | "skipCi": true, 136 | "repoType": "github", 137 | "repoHost": "https://github.com", 138 | "projectName": "code-design", 139 | "projectOwner": "nauvalazhar", 140 | "commitType": "docs" 141 | } 142 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Top-most EditorConfig file 2 | root = true 3 | 4 | # Set default charset to UTF-8 5 | [*] 6 | charset = utf-8 7 | indent_style = space 8 | indent_size = 2 9 | 10 | # End of EditorConfig file 11 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/submit-ui-design.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Submit UI Design 3 | about: Submit your UI design 4 | title: New UI Design Contribution 5 | labels: ui design contribs 6 | assignees: nauvalazhar 7 | 8 | --- 9 | Make sure you have read our [contributing guidelines](https://github.com/nauvalazhar/code-design/blob/main/CONTRIBUTING_DESIGN.md) before submitting this issue. 10 | 11 | **Name** 12 | Your UI design project name. Must be short. 13 | 14 | **Figma Community URL** 15 | Your UI design URL to the Figma community. 16 | 17 | **Your Figma Profile** 18 | The URL of your Figma profile. For example, https://figma.com/@mhd 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | .pnpm-debug.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | if [ -t 2 ]; then 5 | exec >/dev/tty 2>&1 6 | fi 7 | 8 | yarn lint-staged 9 | -------------------------------------------------------------------------------- /.husky/pre-push: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | if [ -t 2 ]; then 5 | exec >/dev/tty 2>&1 6 | fi 7 | 8 | yarn build -------------------------------------------------------------------------------- /.lintstagedrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // Lint & Prettify TSX, TS, JSX, and JS files 3 | '**/*.(tsx|ts|jsx|js)': filenames => [ 4 | `yarn lint`, 5 | `yarn prettier --config ./.prettier.config.js -w ${filenames.join(' ')}` 6 | ], 7 | 8 | // Prettify only JSON files 9 | '**/*.(json)': filenames => `yarn prettier -w ${filenames.join(' ')}` 10 | }; 11 | -------------------------------------------------------------------------------- /.prettier.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | singleQuote: true, 3 | trailingComma: 'none', 4 | arrowParens: 'avoid', 5 | proseWrap: 'preserve', 6 | quoteProps: 'as-needed', 7 | bracketSameLine: false, 8 | bracketSpacing: true, 9 | tabWidth: 2, 10 | importOrder: ['^data/(.*)$', '^components/(.*)$', '^[./]'], 11 | importOrderBuiltinModulesToTop: true, 12 | importOrderCaseInsensitive: true, 13 | importOrderParserPlugins: ['jsx', 'decorators-legacy', 'typescript'], 14 | importOrderMergeDuplicateImports: true, 15 | importOrderCombineTypeAndValueImports: true, 16 | importOrderSeparation: true, 17 | importOrderSortSpecifiers: true, 18 | plugins: [ 19 | require('prettier-plugin-tailwindcss'), 20 | require('@ianvs/prettier-plugin-sort-imports'), 21 | ], 22 | }; 23 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "node_modules/typescript/lib", 3 | "typescript.enablePromptUseWorkspaceTsdk": true 4 | } 5 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | nauvalazhar2@gmail.com. 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | First of all, thank you for taking your valuable time to contribute. 4 | 5 | Here, we would explain everything about all you need to know to start your first contribution to this project. 6 | 7 | ## UI Design Contribution 8 | For UI design contributions, please consult the [UI Design Contribution Guideline document](https://github.com/nauvalazhar/code-design/blob/main/CONTRIBUTING_DESIGN.md). 9 | 10 | ## Code Contribution 11 | 12 | ### Tech Stacks 13 | 14 | We use Next JS as our front-end framework, there are several reasons why we use Next JS. Objectively, Next JS has a better developer experience compared to others that are available on the market. Subjectively, because it's React, we all love React and so do you. 15 | Besides React, we use Tailwind CSS for our styling tool. We don’t use a UI kit library here, since we design every UIs from scratch. Using a UI kit library in this kind of situation would only bring us to tears. 16 | For any interactive components, we use Radix UI as our headless components. 17 | 18 | ### Requirements 19 | 20 | Since the project is still not Dockerized yet, so you have to install all the following requirements into your machine: 21 | 22 | - Node JS v16 23 | - Yarn 24 | 25 | ### How To Run The Front-end App 26 | 27 | Since we use Next JS as our framework, the development workflow is simple: 28 | 29 | 1. Fork this repository 30 | 2. Run the `cd` command into the project directory like `cd code-design` 31 | 3. Run the `yarn` command to start installing all the Node JS dependencies 32 | 4. To start the development server, run the `yarn dev` command 33 | 34 | ### How To Contribute 35 | 36 | There are no formal steps that you need to follow to contribute, but here are some tips: 37 | - Find some opening issues 38 | - Have some discussion about the issue before you working on 39 | - Confirm to the maintainers that you will working on the issue 40 | 41 | Worth noting, please **do not** implement any new feature without opening a new issue first and have disscussion with the maintainers. 42 | 43 | ### Commit Your Changes 44 | 45 | You need to follow [conventional commits](https://www.conventionalcommits.org/) for your commit message before you push all the changes. 46 | 47 | ### Submit Pull Request 48 | 49 | To help the maintainers review your changes, you could follow the following tips: 50 | - Write a detailed description if you made several changes in a single pull request 51 | - You may make changes to some component's interactivity, please attach a related Gif or video so that the maintainers know the expected behavior 52 | - Please attach a screenshot if your changes related to the UI in general 53 | - Please put any link reference if you think that would help the maintainers 54 | 55 | Currently, the only maintainer is [@nauvalazhar](https://github.com/nauvalazhar), so if your pull request has not been reviewed within 24 hours, feel free to mention him on Twitter or any other platform that he lives on. 56 | 57 | --- 58 | 59 | Thank you for any kind of contribution to the project, it helps us to grow the project faster! 60 | -------------------------------------------------------------------------------- /CONTRIBUTING_DESIGN.md: -------------------------------------------------------------------------------- 1 | # UI Design Contribution Guideline 2 | Thank you for your interest in contributing to the design collection! 3 | The purpose of this guide is to provide you with the information you need to make meaningful contributions to the project. 4 | 5 | ## Design Guidelines 6 | In order to maintain a consistent style and user experience across the designs in the collection, please follow these design guidelines: 7 | - Use Figma. We all here love Figma, so you are. 8 | - Create at least two pages: 9 | - **Thumbnail**: Contains all of your design thumbnail information, such as Figma Community file cover, Dribbble shot cover, etc. 10 | - **UI Design** or **Design**: Contains all of your UI design frames, contents, components, etc. 11 | - Publish your UI design as a Figma Community file. 12 | 13 | We don't have any design standards for now, but you may follow these general design tips from us: 14 | - Use a clean design style that focuses on readability and usability. 15 | - Use a limited typography that is consistent with your overall UI design. 16 | - Avoid clutter and distractions. Keep the UI design focused on the most important information. 17 | - Consider the user's experience and how they will interact with the design. 18 | 19 | ## Submitting a New Design 20 | To contribute a new design to the collection, follow these steps: 21 | 1. Go to the [New Issue page](https://github.com/nauvalazhar/code-design/issues/new/choose) 22 | 2. Select the "Submit UI Design" template 23 | 3. Fill out all the questions 24 | 4. Wait for the review process 25 | 26 | ## Review Process 27 | The repository maintainers will review your issue and provide feedback or suggestions for changes. If your design is accepted, it will be published into our site. If changes are needed, you'll be asked to make them and let the maintainers know. 28 | 29 | ## Conclusion 30 | Thank you for your contributions! By submitting a new design, you are helping to make the collection even better and more valuable to users. We look forward to reviewing your UI design contribution! 31 | 32 | > This guideline is new, you may see old UI designs are not following these guidelines. 33 | -------------------------------------------------------------------------------- /FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [nauvalazhar] 2 | custom: https://trakteer.id/mhdnauvalazhar 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Muhamad Nauval Azhar 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /app/(authenticated)/layout.tsx: -------------------------------------------------------------------------------- 1 | import AuthNavs from 'components/AuthNav'; 2 | import Box, { BoxTab, BoxTabs } from 'components/Box'; 3 | import Link from 'next/link'; 4 | 5 | export default function Layout({ children }: { children: React.ReactNode }) { 6 | return ( 7 | 8 | 9 | {children} 10 | 11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /app/(authenticated)/me/page.tsx: -------------------------------------------------------------------------------- 1 | import { redirect } from 'next/navigation'; 2 | import { getAuthUser } from 'services/auth-service'; 3 | 4 | import { BoxTitle } from 'components/Box'; 5 | import { Field, Input, Label } from 'components/Form'; 6 | 7 | export const metadata = { 8 | title: 'Profile' 9 | }; 10 | 11 | export default async function Page() { 12 | const user = await getAuthUser(); 13 | 14 | if (true) { 15 | redirect('/construction'); 16 | } 17 | 18 | return ( 19 | <> 20 | Profile 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |

34 | We're still working on this page. Currently you can only view your 35 | profile. 36 |

37 | 38 | ); 39 | } 40 | -------------------------------------------------------------------------------- /app/(authenticated)/me/submission/[[...params]]/page.tsx: -------------------------------------------------------------------------------- 1 | import { SubmissionMode } from 'app/actions'; 2 | import { notFound } from 'next/navigation'; 3 | import { getAuthId, isUserReviewer } from 'services/auth-service'; 4 | import { getSubmissionBySlug } from 'services/submission-service'; 5 | 6 | import Box, { BoxTitle } from 'components/Box'; 7 | import SubmissionForm from 'components/SubmissionForm'; 8 | 9 | export const metadata = { 10 | title: 'Submission' 11 | }; 12 | 13 | export default async function Page({ 14 | params: { params } 15 | }: { 16 | params: { params: string[] }; 17 | }) { 18 | const slug = params[0]; 19 | let mode = (params[1] || 'edit') as SubmissionMode; 20 | const submission = await getSubmissionBySlug(slug); 21 | const isReviewer = isUserReviewer(); 22 | const userId = await getAuthId(); 23 | 24 | if (isReviewer) { 25 | mode = 'review'; 26 | } 27 | 28 | if ( 29 | (mode === 'review' && !isReviewer) || 30 | (submission.reviewer && submission.reviewer?.userId !== userId) 31 | ) { 32 | notFound(); 33 | } 34 | 35 | return ( 36 |
37 | {mode} Submission 38 | 52 |
53 | ); 54 | } 55 | -------------------------------------------------------------------------------- /app/(authenticated)/me/submissions/[[...params]]/page.tsx: -------------------------------------------------------------------------------- 1 | import clsx from 'clsx'; 2 | import { dateFormat } from 'lib/utils'; 3 | import Link from 'next/link'; 4 | import { notFound } from 'next/navigation'; 5 | import { getAuthUser, isUserReviewer } from 'services/auth-service'; 6 | import { getSubmissions } from 'services/submission-service'; 7 | 8 | import { BoxDescription, BoxTitle } from 'components/Box'; 9 | import { Button } from 'components/Button'; 10 | import SubmissionModeCheckbox from 'components/SubmissionModeCheckbox'; 11 | 12 | export const metadata = { 13 | title: 'Submissions' 14 | }; 15 | 16 | export default async function Page({ 17 | params: { params } 18 | }: { 19 | params: { params: string[] }; 20 | }) { 21 | const mode = params ? params[0] : 'owner'; 22 | const submissions = await getSubmissions({ 23 | where: { 24 | mode: mode === 'reviewer' ? 'reviewer' : 'owner' 25 | } 26 | }); 27 | 28 | const user = await getAuthUser(); 29 | const isReviewer = await isUserReviewer(); 30 | 31 | if (mode === 'reviewer' && !isReviewer) { 32 | notFound(); 33 | } 34 | 35 | return ( 36 | <> 37 | 38 | Submissions 39 | 40 | {mode === 'reviewer' 41 | ? 'Here you can review the submissions that are pending for your review.' 42 | : 'Here you can see all your submissions.'} 43 | 44 | 45 |
46 | {submissions.map(submission => ( 47 |
55 |
56 |

{submission.challenge.name}

57 |

58 | {dateFormat(submission.createdAt)} - {submission.phase} -{' '} 59 | {submission.user.name} 60 |

61 |
62 |
63 | {isReviewer && submission.phase === 'submitted' && ( 64 | 69 | )} 70 | 71 | 74 |
75 |
76 | ))} 77 |
78 | 79 | ); 80 | } 81 | -------------------------------------------------------------------------------- /app/actions.ts: -------------------------------------------------------------------------------- 1 | 'use server'; 2 | 3 | import { redirect } from 'next/navigation'; 4 | import { techIdsSchema } from 'schemas/submission_techs'; 5 | import { insertSubmissionSchema, NewSubmission } from 'schemas/submissions'; 6 | import { Tech } from 'schemas/techs'; 7 | import { isUserReviewer } from 'services/auth-service'; 8 | import { 9 | createSubmission, 10 | updateSubmission 11 | } from 'services/submission-service'; 12 | import { getTechs } from 'services/tech-service'; 13 | 14 | export type SubmissionMode = 'create' | 'edit' | 'review'; 15 | 16 | export async function handleCreateSubmission( 17 | challenge: string, 18 | mode: SubmissionMode, 19 | prevState: any, 20 | formData: FormData 21 | ) { 22 | const demo = formData.get('demo') as string; 23 | const repository = formData.get('repository') as string; 24 | const description = formData.get('description') as string; 25 | const techs = formData.get('techs') as string; 26 | 27 | const parse = insertSubmissionSchema.safeParse({ 28 | demo, 29 | repository, 30 | description 31 | }); 32 | 33 | if (parse.success === false) { 34 | return { 35 | success: false, 36 | error: parse.error.flatten().fieldErrors 37 | }; 38 | } 39 | 40 | const parseTechs = techIdsSchema.safeParse({ 41 | techs 42 | }); 43 | 44 | if (parseTechs.success === false) { 45 | return { 46 | success: false, 47 | error: parseTechs.error.flatten().fieldErrors 48 | }; 49 | } 50 | 51 | await createSubmission({ 52 | demo, 53 | repository, 54 | description, 55 | challenge, 56 | techs: parseTechs.data.techs 57 | }); 58 | 59 | return { 60 | success: true 61 | }; 62 | } 63 | 64 | export async function handleUpdateSubmission( 65 | submissionId: NewSubmission['id'], 66 | mode: SubmissionMode, 67 | prevState: any, 68 | formData: FormData 69 | ) { 70 | const demo = formData.get('demo') as string; 71 | const repository = formData.get('repository') as string; 72 | const description = formData.get('description') as string; 73 | const techs = formData.get('techs') as string; 74 | const image = formData.get('image') as File; 75 | const phase = formData.get('phase') as NewSubmission['phase']; 76 | const note = formData.get('note') as string; 77 | const imageOld = formData.get('image_old') as string; 78 | 79 | const parse = insertSubmissionSchema.safeParse({ 80 | demo, 81 | repository, 82 | description, 83 | image, 84 | phase, 85 | note 86 | }); 87 | 88 | if (parse.success === false) { 89 | return { 90 | success: false, 91 | error: parse.error.flatten().fieldErrors 92 | }; 93 | } 94 | 95 | const parseTechs = techIdsSchema.safeParse({ 96 | techs 97 | }); 98 | 99 | if (parseTechs.success === false) { 100 | return { 101 | success: false, 102 | error: parseTechs.error.flatten().fieldErrors 103 | }; 104 | } 105 | 106 | await updateSubmission( 107 | { 108 | id: submissionId, 109 | demo, 110 | repository, 111 | description, 112 | phase, 113 | note, 114 | image, 115 | techs: parseTechs.data.techs, 116 | imageOld 117 | }, 118 | mode === 'review' 119 | ); 120 | 121 | const isReviewer = await isUserReviewer(); 122 | 123 | if (isReviewer) { 124 | redirect('/me/submissions/reviewer'); 125 | } 126 | redirect('/me/submissions'); 127 | } 128 | 129 | export async function loadTechs(search): Promise { 130 | 'use server'; 131 | 132 | return getTechs({ 133 | limit: 5, 134 | where: { 135 | search 136 | } 137 | }); 138 | } 139 | -------------------------------------------------------------------------------- /app/auth/callback/route.ts: -------------------------------------------------------------------------------- 1 | import { encryptData, getSession } from 'lib/session'; 2 | import { redirect } from 'next/navigation'; 3 | import { getOrRegisterUser } from 'services/user-service'; 4 | 5 | export async function GET(request: Request) { 6 | const session = await getSession(); 7 | 8 | const query = new URL(request.url).searchParams; 9 | const code = query.get('code'); 10 | 11 | const accessToken = await fetch( 12 | 'https://github.com/login/oauth/access_token', 13 | { 14 | method: 'POST', 15 | headers: { 16 | 'Content-Type': 'application/json', 17 | Accept: 'application/json' 18 | }, 19 | body: JSON.stringify({ 20 | client_id: process.env.GITHUB_ID, 21 | client_secret: process.env.GITHUB_SECRET, 22 | code 23 | }) 24 | } 25 | ); 26 | 27 | const result = await accessToken.json(); 28 | 29 | if (result.error) { 30 | return new Response(result.error, { status: 400 }); 31 | } 32 | 33 | const resultUser = await fetch('https://api.github.com/user', { 34 | headers: { 35 | Authorization: `Bearer ${result.access_token}` 36 | } 37 | }); 38 | const userInfo = await resultUser.json(); 39 | 40 | const resultEmails = await fetch('https://api.github.com/user/emails', { 41 | headers: { 42 | Authorization: `Bearer ${result.access_token}` 43 | } 44 | }); 45 | const emails = await resultEmails.json(); 46 | 47 | const [user] = await getOrRegisterUser({ 48 | username: userInfo.login, 49 | email: emails.find((email: any) => email.primary)?.email, 50 | name: userInfo.name || userInfo.login, 51 | bio: userInfo.bio, 52 | avatar: userInfo.avatar_url, 53 | githubId: userInfo.id, 54 | hireable: userInfo.hireable, 55 | refreshToken: await encryptData({ 56 | token: result.refresh_token, 57 | expires: result.refresh_token_expires_in 58 | }) 59 | }); 60 | 61 | session.token = result.access_token; 62 | session.expires = result.expires_in; 63 | session.userId = user.id; 64 | 65 | await session.save(); 66 | 67 | redirect('/'); 68 | } 69 | -------------------------------------------------------------------------------- /app/auth/login/page.tsx: -------------------------------------------------------------------------------- 1 | import { redirect } from 'next/navigation'; 2 | import { isAuth } from 'services/auth-service'; 3 | 4 | import Box, { BoxDescription, BoxTitle } from 'components/Box'; 5 | import { Button } from 'components/Button'; 6 | 7 | export const metadata = { 8 | title: 'Login' 9 | }; 10 | 11 | export default async function Page() { 12 | const isLogin = await isAuth(); 13 | 14 | if (isLogin) { 15 | redirect('/me'); 16 | } 17 | 18 | return ( 19 | 20 | Login 21 | You can only login with GitHub for now. 22 | 29 | 30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /app/challenge/[slug]/head.tsx: -------------------------------------------------------------------------------- 1 | // @ts-ignore 2 | import challengeData from 'data/challenges'; 3 | 4 | export default async function Head({ params }) { 5 | const challenge = challengeData.find( 6 | challenge => challenge.slug === params.slug 7 | ); 8 | 9 | return ( 10 | <> 11 | {`${challenge?.name} – Code The Design`} 12 | 13 | 14 | 18 | 19 | 20 | 21 | 22 | 26 | 27 | 28 | 29 | 33 | 34 | 38 | 42 | 46 | 47 | 48 | 52 | 56 | 60 | 64 | 65 | ); 66 | } 67 | -------------------------------------------------------------------------------- /app/challenge/[slug]/submission/page.tsx: -------------------------------------------------------------------------------- 1 | import { notFound } from 'next/navigation'; 2 | 3 | import Box, { BoxTitle } from 'components/Box'; 4 | import SubmissionForm from 'components/SubmissionForm'; 5 | 6 | export const metadata = { 7 | title: 'Submission' 8 | }; 9 | 10 | export default function Page({ 11 | params: { slug } 12 | }: { 13 | params: { slug: string }; 14 | }) { 15 | if (true) { 16 | notFound(); 17 | } 18 | 19 | return ( 20 | 21 | Submission 22 | 23 | 24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /app/challenges/page.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | getChallenges, 3 | type GetChallengesParams 4 | } from 'services/challenge-service'; 5 | 6 | import CategoriesSelector from 'components/CategoriesSelector'; 7 | import Challenges from 'components/Challenges'; 8 | import DifficultiesSelector from 'components/DifficultiesSelector'; 9 | import LoadMore from 'components/LoadMore'; 10 | import { SectionTitle } from 'components/SectionTitle'; 11 | 12 | export const metadata = { 13 | title: 'Challenges' 14 | }; 15 | 16 | async function loadMore(offset, params) { 17 | 'use server'; 18 | 19 | const { challenges } = await getChallenges({ 20 | ...params, 21 | offset 22 | }); 23 | 24 | return ; 25 | } 26 | 27 | export default async function Page({ 28 | searchParams 29 | }: { 30 | searchParams: { [key: string]: string | string[] | undefined }; 31 | }) { 32 | const dataParams: GetChallengesParams = { 33 | where: { 34 | difficultyId: Number(searchParams.difficulty) || undefined, 35 | categoryId: Number(searchParams.category) || undefined 36 | } 37 | }; 38 | 39 | const { challenges, limit, total } = await getChallenges(dataParams); 40 | 41 | return ( 42 |
43 |
44 | Challenges 45 |
46 | 47 |
48 |
49 | 50 |
51 |
52 | 53 | 60 | 61 | 62 |
63 | ); 64 | } 65 | -------------------------------------------------------------------------------- /app/code-the-design.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nauvalazhar/code-design/7017111bfa383ea4cf0e4bd6072fb5fc9239102e/app/code-the-design.ico -------------------------------------------------------------------------------- /app/construction/page.tsx: -------------------------------------------------------------------------------- 1 | import Box, { BoxDescription, BoxTitle } from 'components/Box'; 2 | 3 | export default function Page() { 4 | return ( 5 | 6 | Construction 7 | This page is under construction. 8 | 9 | ); 10 | } 11 | -------------------------------------------------------------------------------- /app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nauvalazhar/code-design/7017111bfa383ea4cf0e4bd6072fb5fc9239102e/app/favicon.ico -------------------------------------------------------------------------------- /app/globals.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: #191919; 3 | background-image: url('/codedesign-bg.png'); 4 | background-position: center; 5 | background-attachment: fixed; 6 | background-size: cover; 7 | background-repeat: no-repeat; 8 | 9 | @apply text-body; 10 | } 11 | 12 | .gradient-blue { 13 | background-image: linear-gradient(90.34deg, #4375f2 0%, #4375f2 100%); 14 | } 15 | 16 | .button-shadow { 17 | box-shadow: 0px 8px 4px rgba(0, 0, 0, 0.15), 0px 4px 0px #2854c3; 18 | } 19 | 20 | .button-text-shadow { 21 | text-shadow: 0px 2px 6px rgba(0, 0, 0, 0.2); 22 | } 23 | 24 | .text-shadow { 25 | text-shadow: 0px 2px 4px rgba(0, 0, 0, 0.25); 26 | } 27 | -------------------------------------------------------------------------------- /app/layout.tsx: -------------------------------------------------------------------------------- 1 | import 'tailwindcss/tailwind.css'; 2 | 3 | import clsx from 'clsx'; 4 | import { Metadata } from 'next'; 5 | import { Fredoka, Patrick_Hand } from 'next/font/google'; 6 | import { PropsWithChildren } from 'react'; 7 | 8 | import { AnalyticsWrapper } from 'components/Analytics'; 9 | import ScrollToTop from 'components/ScrollToTop'; 10 | 11 | import './globals.css'; 12 | 13 | import Sidebar from 'components/Sidebar'; 14 | 15 | const fontDisplay = Fredoka({ 16 | variable: '--font-display', 17 | weight: '500', 18 | subsets: ['latin'] 19 | }); 20 | 21 | const fontSans = Fredoka({ 22 | variable: '--font-sans', 23 | weight: ['400', '500', '600', '700'], 24 | subsets: ['latin'] 25 | }); 26 | 27 | export const metadata: Metadata = { 28 | title: { 29 | template: '%s – codedesign.dev', 30 | default: 'Untitled' 31 | } 32 | }; 33 | 34 | export default function RootLayout({ children }: PropsWithChildren) { 35 | return ( 36 | 43 | 44 |
45 | 46 |
47 |
{children}
48 |
49 | {/**/} 67 |
68 | 69 | 70 | 71 | 72 | ); 73 | } 74 | -------------------------------------------------------------------------------- /app/manual/page.tsx: -------------------------------------------------------------------------------- 1 | import clsx from 'clsx'; 2 | 3 | import Box, { BoxTitle } from 'components/Box'; 4 | 5 | export default async function Page() { 6 | return ( 7 | 8 | Manual 9 |
10 |

Motivation

11 |

12 | As a developer, it's important to continually challenge yourself 13 | and improve your skills. Code The Design was created with this goal in 14 | mind. We believe that the best way to learn and grow is by tackling 15 | real-life projects and challenges, rather than just following along 16 | with tutorials or practicing with contrived exercises. 17 |

18 |

19 | That's why we've created a platform that offers a wide 20 | variety of challenges, inspired by real-world designs and use cases. 21 | These challenges are meant to push you to think creatively, 22 | problem-solve, and develop your skills as a developer. 23 |

24 |

25 | We understand that everyone has different levels of experience and 26 | expertise, which is why we've included a subjective difficulty 27 | rating for each challenge. This allows you to choose challenges that 28 | are appropriate for your current skill level, and gradually work your 29 | way up as you improve. 30 |

31 |

32 | We also understand that the website is in a very early stage and far 33 | from being stable. Our focus is on getting the website visible as soon 34 | as possible, which is why we don't currently offer pre-exported 35 | assets and specific objectives for the challenges. However, we provide 36 | Figma design files for each challenge, which you can learn how the 37 | designs work and use to export any assets you need. 38 |

39 |

40 | We hope that Code The Design will serve as a valuable resource for you 41 | as you work to improve your skills and become a more confident and 42 | capable developer. Happy coding! 43 |

44 |

Technologies

45 |

46 | You have the freedom to choose the technologies that you feel 47 | comfortable using in order to complete the challenges on our website. 48 | We do not have a preferred tech stack, and we welcome developers from 49 | all backgrounds and skill levels to participate in our challenges. 50 | Whether you prefer to use front-end technologies like HTML, CSS, and 51 | JavaScript, or back-end technologies like PHP, Python, or Ruby, you 52 | are free to use whatever tools and frameworks you feel most 53 | comfortable with. Our goal is to provide you with real-life use case 54 | challenges that allow you to showcase your skills and abilities, and 55 | we believe that the best way to do this is by giving you the freedom 56 | to choose the technologies that work best for you. 57 |

58 |

Contribution

59 |

60 | We are glad that you are interested in contributing to our project. As 61 | a reminder, our project is open source and we welcome contributions 62 | from anyone who is interested in helping us build a better product. 63 |

64 |

65 | We believe that open source projects thrive when there is a diverse 66 | range of perspectives and skillsets involved. Whether you are a 67 | developer, designer, or simply have an idea that you think could 68 | improve the project, we encourage you to reach out and get involved.{' '} 69 |

70 |

71 | There are many ways you can contribute to our project. You can submit 72 | bug reports and feature requests, help us by testing new features and 73 | fixing issues, or even contribute code or design assets directly. No 74 | matter how you choose to get involved, your contribution will make a 75 | difference and help us to continue building a great product. 76 |

77 |
78 |
79 | ); 80 | } 81 | -------------------------------------------------------------------------------- /app/page.tsx: -------------------------------------------------------------------------------- 1 | import { Arrow, Interfaces } from 'doodle-icons'; 2 | import type { Metadata } from 'next'; 3 | import Image from 'next/image'; 4 | import Link from 'next/link'; 5 | import sectionHeaderQuests from 'public/section-header-quests.png'; 6 | import { getChallenges } from 'services/challenge-service'; 7 | 8 | import Challenges from 'components/Challenges'; 9 | import Designers from 'components/Designers'; 10 | import { SectionTitle } from 'components/SectionTitle'; 11 | 12 | export const metadata: Metadata = { 13 | title: 14 | 'Level up your coding skills with hands-on design challenges – codedesign.dev', 15 | description: 'Level up your coding skills with hands-on design challenges.', 16 | keywords: 'code the design, design with figma, design web, design challange', 17 | openGraph: { 18 | type: 'website', 19 | locale: 'en_US', 20 | url: 'https://codedesign.dev/', 21 | siteName: 'codedesign.dev', 22 | title: 'codedesign.dev', 23 | description: 'Level up your coding skills with hands-on design challenges.', 24 | images: ['https://codedesign.dev/code-design.png'] 25 | } 26 | }; 27 | 28 | export default async function Home() { 29 | const { challenges } = await getChallenges({ 30 | limit: 2 31 | }); 32 | 33 | return ( 34 |
35 |
36 | Challenges 37 | 41 | View all challenges 42 | 43 | 44 |
45 |
46 | 47 |
48 | 49 |
50 | Designers 51 |
52 | 53 |
54 | ); 55 | } 56 | -------------------------------------------------------------------------------- /app/submissions/page.tsx: -------------------------------------------------------------------------------- 1 | export default function Page() { 2 | return
Submissions
; 3 | } 4 | -------------------------------------------------------------------------------- /app/test-1/page.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import Link from 'next/link'; 4 | import { useState } from 'react'; 5 | 6 | export default function Page() { 7 | const [count, setCount] = useState(0); 8 | 9 | return ( 10 |
11 | {count} 12 | 13 | 14 | Test 2 15 |
16 | ); 17 | } 18 | -------------------------------------------------------------------------------- /app/test-2/page.tsx: -------------------------------------------------------------------------------- 1 | import { getChallenges } from 'services/challenge-service'; 2 | 3 | export default async function Page() { 4 | const challenges = await getChallenges(); 5 | 6 | return

Test 2

; 7 | } 8 | -------------------------------------------------------------------------------- /app/test/route.ts: -------------------------------------------------------------------------------- 1 | import { getSession } from 'lib/session'; 2 | 3 | export async function GET() { 4 | const session = await getSession(); 5 | 6 | const response = await fetch('https://api.github.com/user/emails', { 7 | headers: { 8 | Authorization: `Bearer ${session.token}`, 9 | }, 10 | }); 11 | 12 | const data = await response.json(); 13 | 14 | return Response.json(data); 15 | } 16 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { extends: ['@commitlint/config-conventional'] }; 2 | -------------------------------------------------------------------------------- /components/Analytics.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { Analytics } from '@vercel/analytics/react'; 4 | 5 | export function AnalyticsWrapper() { 6 | return ; 7 | } 8 | -------------------------------------------------------------------------------- /components/AuthNav.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { BoxTab, BoxTabs } from 'components/Box'; 4 | import Link from 'next/link'; 5 | import { usePathname } from 'next/navigation'; 6 | 7 | const AuthNavs = () => { 8 | const pathname = usePathname(); 9 | 10 | return ( 11 | 12 | 13 | Profile 14 | 15 | 16 | Submissions 17 | 18 | 19 | ); 20 | }; 21 | 22 | export default AuthNavs; 23 | -------------------------------------------------------------------------------- /components/Box.tsx: -------------------------------------------------------------------------------- 1 | import { Slot } from '@radix-ui/react-slot'; 2 | import clsx from 'clsx'; 3 | 4 | type BoxProps = React.HTMLAttributes; 5 | 6 | const Box = (props: BoxProps) => { 7 | return ( 8 |
16 | ); 17 | }; 18 | 19 | type BoxTitleProps = React.HTMLAttributes; 20 | 21 | export const BoxTitle = ({ className, ...props }: BoxTitleProps) => { 22 | return ( 23 |

30 | ); 31 | }; 32 | 33 | export const BoxDescription = ({ children }: { children: React.ReactNode }) => { 34 | return ( 35 |

{children}

36 | ); 37 | }; 38 | 39 | export const BoxTabs = ({ children }: { children: React.ReactNode }) => { 40 | return ( 41 |
42 | {children} 43 |
44 | ); 45 | }; 46 | 47 | type BoxTabProps = React.HTMLAttributes & { 48 | asChild?: boolean; 49 | active?: boolean; 50 | }; 51 | 52 | export const BoxTab = ({ 53 | children, 54 | asChild = false, 55 | active, 56 | ...props 57 | }: BoxTabProps) => { 58 | const Comp = asChild ? Slot : 'button'; 59 | 60 | return ( 61 | 70 | {children} 71 | 72 | ); 73 | }; 74 | 75 | export default Box; 76 | -------------------------------------------------------------------------------- /components/Button.tsx: -------------------------------------------------------------------------------- 1 | import { Slot } from '@radix-ui/react-slot'; 2 | import { cva, type VariantProps } from 'class-variance-authority'; 3 | import { Interfaces } from 'doodle-icons'; 4 | import { cn } from 'lib/utils'; 5 | import * as React from 'react'; 6 | 7 | const buttonVariants = cva( 8 | 'flex font-display items-center justify-center gap-4 uppercase', 9 | { 10 | variants: { 11 | variant: { 12 | default: 13 | 'border-transparent button-shadow button-text-shadow gradient-blue rounded-xl text-brand-light', 14 | secondary: 'border-black bg-orange-400', 15 | danger: 'border-black bg-red-500 text-brand' 16 | }, 17 | size: { 18 | sm: 'border-2 px-4 py-2 text-lg', 19 | md: 'border-2 px-6 py-3 text-xl', 20 | default: 'border-2 px-8 py-4 text-xl' 21 | } 22 | }, 23 | defaultVariants: { 24 | variant: 'default', 25 | size: 'default' 26 | } 27 | } 28 | ); 29 | 30 | export interface ButtonProps 31 | extends React.ButtonHTMLAttributes, 32 | VariantProps { 33 | asChild?: boolean; 34 | progress?: boolean; 35 | } 36 | 37 | const Button = React.forwardRef( 38 | ( 39 | { 40 | className, 41 | variant, 42 | size, 43 | asChild = false, 44 | progress, 45 | children, 46 | disabled, 47 | ...props 48 | }, 49 | ref 50 | ) => { 51 | const Comp = asChild ? Slot : 'button'; 52 | return ( 53 | 59 | {progress ? ( 60 | 64 | ) : ( 65 | children 66 | )} 67 | 68 | ); 69 | } 70 | ); 71 | Button.displayName = 'Button'; 72 | 73 | export { Button, buttonVariants }; 74 | -------------------------------------------------------------------------------- /components/CategoriesSelector.tsx: -------------------------------------------------------------------------------- 1 | import CategoriesSelectorClient from 'components/CategoriesSelectorClient'; 2 | import { getCategories } from 'services/category-service'; 3 | 4 | const CategoriesSelector = async () => { 5 | const categories = await getCategories(); 6 | 7 | return ; 8 | }; 9 | 10 | export default CategoriesSelector; 11 | -------------------------------------------------------------------------------- /components/CategoriesSelectorClient.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { useSearchParamsManipulator } from 'lib/hooks/useSearchParamsManipulator'; 4 | import type { Category } from 'schemas/categories'; 5 | 6 | import { 7 | Select, 8 | SelectContent, 9 | SelectItem, 10 | SelectTrigger 11 | } from 'components/Select'; 12 | 13 | const CategoriesSelectorClient = ({ 14 | categories 15 | }: { 16 | categories: Category[]; 17 | }) => { 18 | const { setSearchParams, searchParams } = useSearchParamsManipulator(); 19 | 20 | function handleSelect(category) { 21 | if (category === '-1') { 22 | category = undefined; 23 | } 24 | 25 | setSearchParams({ 26 | category 27 | }); 28 | } 29 | 30 | return ( 31 | 45 | ); 46 | }; 47 | 48 | export default CategoriesSelectorClient; 49 | -------------------------------------------------------------------------------- /components/Challenge.tsx: -------------------------------------------------------------------------------- 1 | import clsx from 'clsx'; 2 | import { Arrow, Interfaces } from 'doodle-icons'; 3 | import Image from 'next/image'; 4 | import Link from 'next/link'; 5 | 6 | export type ChallengeProps = { 7 | name: string; 8 | difficulty: string; 9 | category: string; 10 | accent: string; 11 | accent2: string; 12 | image: string; 13 | slug: string; 14 | designers: string[]; 15 | }; 16 | 17 | function Challenge({ 18 | name, 19 | difficulty, 20 | category, 21 | accent, 22 | accent2, 23 | image, 24 | slug, 25 | designers 26 | }: ChallengeProps) { 27 | return ( 28 | 29 |
41 | {name} 46 |

{name}

47 |

48 | Design by {designers[0]}{' '} 49 | {designers.length > 1 && `+ ${designers.length - 1} others`} 50 |

51 |
52 | 57 | 62 |
63 |
64 | 65 | ); 66 | } 67 | 68 | type MetaProps = { 69 | icon: React.ElementType>; 70 | name: string; 71 | value: string; 72 | }; 73 | 74 | function Meta({ icon: Icon, name, value }: MetaProps) { 75 | return ( 76 |
77 | 78 |
79 | {name} 80 | {value} 81 |
82 |
83 | ); 84 | } 85 | 86 | export default Challenge; 87 | -------------------------------------------------------------------------------- /components/Challenges.tsx: -------------------------------------------------------------------------------- 1 | import { getChallenges, JoinedChallenge } from 'services/challenge-service'; 2 | 3 | import { Button } from 'components/Button'; 4 | import Challenge from 'components/Challenge'; 5 | 6 | const Challenges = ({ challenges }: { challenges: JoinedChallenge[] }) => { 7 | return ( 8 | <> 9 | {challenges.map( 10 | ({ 11 | name, 12 | image, 13 | accent, 14 | accent2, 15 | slug, 16 | category, 17 | difficulty, 18 | designers 19 | }) => ( 20 | name)} 30 | /> 31 | ) 32 | )} 33 | 34 | ); 35 | }; 36 | 37 | export default Challenges; 38 | -------------------------------------------------------------------------------- /components/Combobox.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import clsx from 'clsx'; 4 | import { Command, useCommandState, type CommandRoot } from 'cmdk'; 5 | import { inputStyle } from 'components/Form'; 6 | 7 | type ComboboxProps = React.ComponentProps; 8 | 9 | export const Combobox = ({ children, ...props }: ComboboxProps) => { 10 | return ( 11 | 12 | {children} 13 | 14 | ); 15 | }; 16 | 17 | type ComboboxListProps = React.ComponentProps; 18 | 19 | export const ComboboxList = (props: ComboboxListProps) => { 20 | const search = useCommandState((state) => state.search); 21 | 22 | return ( 23 | 31 | ); 32 | }; 33 | 34 | type ComboboxInputProps = React.ComponentProps; 35 | 36 | export const ComboboxInput = (props: ComboboxInputProps) => { 37 | return ; 38 | }; 39 | 40 | type ComboboxItemProps = React.ComponentProps; 41 | 42 | export const ComboboxItem = (props: ComboboxItemProps) => { 43 | return ( 44 | 48 | ); 49 | }; 50 | 51 | type ComboboxLoadingProps = React.ComponentProps; 52 | 53 | export const ComboboxLoading = (props: ComboboxLoadingProps) => { 54 | return ( 55 | 59 | ); 60 | }; 61 | 62 | type ComboboxEmptyProps = React.ComponentProps; 63 | 64 | export const ComboboxEmpty = (props: ComboboxEmptyProps) => { 65 | return ; 66 | }; 67 | -------------------------------------------------------------------------------- /components/Designers.tsx: -------------------------------------------------------------------------------- 1 | import Image from 'next/image'; 2 | import { getDesigners } from 'services/user-service'; 3 | 4 | const Designers = async () => { 5 | const designers = await getDesigners(); 6 | 7 | return ( 8 |
9 | {designers.map(designer => ( 10 |
14 | {designer.name} 19 |

{designer.name}

20 |
21 | ))} 22 |
23 | ); 24 | }; 25 | 26 | export default Designers; 27 | -------------------------------------------------------------------------------- /components/DifficultiesSelector.tsx: -------------------------------------------------------------------------------- 1 | import DifficultiesSelectorClient from 'components/DifficultiesSelectorClient'; 2 | import { getDifficulties } from 'services/difficulty-service'; 3 | 4 | const DifficultiesSelector = async () => { 5 | const difficulties = await getDifficulties(); 6 | 7 | return ; 8 | }; 9 | 10 | export default DifficultiesSelector; 11 | -------------------------------------------------------------------------------- /components/DifficultiesSelectorClient.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { useSearchParamsManipulator } from 'lib/hooks/useSearchParamsManipulator'; 4 | import type { Difficulty } from 'schemas/difficulties'; 5 | 6 | import { 7 | Select, 8 | SelectContent, 9 | SelectItem, 10 | SelectTrigger 11 | } from 'components/Select'; 12 | 13 | const DifficultiesSelectorClient = ({ 14 | difficulties 15 | }: { 16 | difficulties: Difficulty[]; 17 | }) => { 18 | const { setSearchParams, searchParams } = useSearchParamsManipulator(); 19 | 20 | function handleSelect(difficulty) { 21 | if (difficulty === '-1') { 22 | difficulty = undefined; 23 | } 24 | 25 | setSearchParams({ 26 | difficulty 27 | }); 28 | } 29 | 30 | return ( 31 | 45 | ); 46 | }; 47 | 48 | export default DifficultiesSelectorClient; 49 | -------------------------------------------------------------------------------- /components/Dropdown.tsx: -------------------------------------------------------------------------------- 1 | import * as RadixDropdown from '@radix-ui/react-dropdown-menu'; 2 | import clsx from 'clsx'; 3 | import { ChevronDown } from 'lucide-react'; 4 | 5 | const Dropdown = ({ children }: { children: React.ReactNode }) => { 6 | return {children}; 7 | }; 8 | 9 | const DropdownTrigger = ({ children }: { children: React.ReactNode }) => { 10 | return ( 11 | 12 | {children} 13 | 14 | 15 | ); 16 | }; 17 | 18 | const DropdownContent = ({ children }: { children: React.ReactNode }) => { 19 | return ( 20 | 21 | 24 | {children} 25 | 26 | 27 | ); 28 | }; 29 | 30 | const DropdownItem = ({ 31 | children, 32 | ...props 33 | }: RadixDropdown.DropdownMenuItemProps) => { 34 | return ( 35 | 41 | {children} 42 | 43 | ); 44 | }; 45 | 46 | export { Dropdown, DropdownTrigger, DropdownContent, DropdownItem }; 47 | -------------------------------------------------------------------------------- /components/EmptyState.tsx: -------------------------------------------------------------------------------- 1 | import Image from 'next/image'; 2 | 3 | const EmptyState = ({ children }) => { 4 | return
{children}
; 5 | }; 6 | 7 | const EmptyStateTitle = ({ children }) => { 8 | return

{children}

; 9 | }; 10 | 11 | const EmptyStateDescription = ({ children }) => { 12 | return

{children}

; 13 | }; 14 | 15 | const EmptyStateImage = ({ src, alt }) => { 16 | return {alt}; 17 | }; 18 | 19 | export { EmptyState, EmptyStateTitle, EmptyStateDescription, EmptyStateImage }; 20 | 21 | export default EmptyState; 22 | -------------------------------------------------------------------------------- /components/FigmaPreview.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import clsx from 'clsx'; 4 | import { Interfaces } from 'doodle-icons'; 5 | import { useState } from 'react'; 6 | 7 | export type FigmaPreviewProps = { 8 | src: string; 9 | }; 10 | 11 | export default function FigmaPreview({ src }: FigmaPreviewProps) { 12 | const [loading, setLoading] = useState(true); 13 | const id = src.split(/\//).pop(); 14 | 15 | function loaded() { 16 | setLoading(false); 17 | } 18 | 19 | return ( 20 |
27 | {loading && ( 28 |
35 | 39 | Loading Figma Preview 40 |
41 | )} 42 |