diff --git a/README.md b/README.md index d53a45b..1acd5e8 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,31 @@ # What Should I Design? 🎨 + [![Build & Deploy 🚀](https://github.com/MartinsOnuoha/what-should-i-design/actions/workflows/firebase-hosting-merge.yml/badge.svg)](https://github.com/MartinsOnuoha/what-should-i-design/actions/workflows/firebase-hosting-merge.yml) -You're fresh out of learning UI/UX Design, you want to get your hands dirty with _almost_ real-world example problems to harness your design skills and build up a portfolio. +You're fresh out of learning UI/UX Design, you want to get your hands dirty with _almost_ real-world example problems to harness your design skills and build up a portfolio. Problem is, You don't know what to design?. - Well [Fake Clients](https://fakeclients.com/) isn't completely Free and is limited, and [WhatshouldIdesign.com](http://www.whatshouldidesign.com/) is all jokes. So here's an alternative completely free and crowdsourced collection of real-world design problems to try. ![what-should-i-design-banner-image](https://whatshouldidesign.space/screen1.webp) ![what-should-i-design-banner-image](https://whatshouldidesign.space/screen2.webp) -![what-should-i-design-banner-image](https://whatshouldidesign.space/screen3.webp) + +## Features +- Community Source UI/UX Design problem Statements +- Community Sourced Samples for each Statement +- Category Specific Statements ## How to Contribute ### Content -You can contribute to this project by sourcing more problem statements from anywhere, or just coming up with yours (you silly genius). -One recommended source would be [ChatGPT](https://chat.openai.com/). -Head over to [OpenAI chat](https://chat.openai.com/) and start a conversation to request as many design problem statement within any of the following categories [below](#problem-statements). +You can contribute to this project by sourcing more problem statements from anywhere, or just coming up with yours (you silly genius). +One recommended source would be [ChatGPT](https://chat.openai.com/). + +Head over to [OpenAI chat](https://chat.openai.com/) and start a conversation to request as many design problem statement within any of the following categories [below](#problem-statements). ### Code + This project runs on Vue and Vite, feel free to create [issues](https://github.com/MartinsOnuoha/what-should-i-design/issues) first. Fork and open MRs for improvements. ## Problem Statements @@ -27,12 +33,12 @@ This project runs on Vue and Vite, feel free to create [issues](https://github.c
🛒 E-commerce -| Title | Description | -|--------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| Artisanal Food Delivery App | Create a food delivery app specializing in artisanal and local food products. Prioritize an appealing marketplace and a simple ordering process. | -| Sustainable Fashion Marketplace | Create a sustainable fashion e-commerce platform that connects environmentally conscious shoppers with eco-friendly clothing brands. Design a user-friendly shopping experience with a focus on sustainability. | -| Grocery Delivery App | Design a mobile app for a grocery delivery service. Ensure a seamless user experience for browsing products, adding items to the cart, scheduling deliveries, and tracking orders. | -| E-commerce Marketplace for Handmade Crafts | Design an e-commerce platform exclusively for handmade craft products. Focus on creating a visually appealing and user-friendly marketplace for artisans and craft enthusiasts. | +| Title | Description | Samples | +|--------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------| +| Artisanal Food Delivery App | Create a food delivery app specializing in artisanal and local food products. Prioritize an appealing marketplace and a simple ordering process. | | +| Sustainable Fashion Marketplace | Create a sustainable fashion e-commerce platform that connects environmentally conscious shoppers with eco-friendly clothing brands. Design a user-friendly shopping experience with a focus on sustainability. | | +| Grocery Delivery App | Design a mobile app for a grocery delivery service. Ensure a seamless user experience for browsing products, adding items to the cart, scheduling deliveries, and tracking orders. | | +| E-commerce Marketplace for Handmade Crafts | Design an e-commerce platform exclusively for handmade craft products. Focus on creating a visually appealing and user-friendly marketplace for artisans and craft enthusiasts. | |
@@ -243,8 +249,6 @@ This project runs on Vue and Vite, feel free to create [issues](https://github.c 🎮 Blockchain and Cryptocurrency Gaming
- - Here's a [sample chat](https://chat.openai.com/share/2aa6268a-7a4e-4740-bca4-f068e100126a) You can open a new MR and update this [README](https://github.com/MartinsOnuoha/what-should-i-design/blob/master/README.md) adding new problem statements to the list above. diff --git a/public/screen2.webp b/public/screen2.webp index 6766625..8958d22 100644 Binary files a/public/screen2.webp and b/public/screen2.webp differ diff --git a/public/screen3.webp b/public/screen3.webp deleted file mode 100644 index 093c8f0..0000000 Binary files a/public/screen3.webp and /dev/null differ diff --git a/schema.graphql b/schema.graphql index bd714c5..fc7a673 100644 --- a/schema.graphql +++ b/schema.graphql @@ -30,7 +30,20 @@ input statements_bool_exp { category_id: Int_comparison_exp } + type Query { categories: [Category!]! statements(where: statements_bool_exp): [Statement!]! } + +input samples_insert_input { + statement_id: ID + url: String +} +type samples_mutation_response { + affected_rows: Int +} + +type Mutation { + insert_samples(objects: [samples_insert_input!]!): samples_mutation_response +} diff --git a/src/assets/styles/color-scheme.scss b/src/assets/styles/color-scheme.scss index 85b74c5..f2ff611 100644 --- a/src/assets/styles/color-scheme.scss +++ b/src/assets/styles/color-scheme.scss @@ -1,4 +1,6 @@ body[color-scheme='dark'] { + @import '@/components/AppAddUrl/AppAddUrl.dark'; + @import '@/components/AppAddUrl/AppAddUrlPreview.dark'; @import '@/components/AppFilter/AppFilter.dark'; @import '@/components/AppHeader/HeaderActions.dark'; @import '@/components/AppSideBanner/AppSideBanner.dark'; @@ -9,6 +11,8 @@ body[color-scheme='dark'] { } body[color-scheme='light'] { + @import '@/components/AppAddUrl/AppAddUrl'; + @import '@/components/AppAddUrl/AppAddUrlPreview'; @import '@/components/AppCategory/AppCategory'; @import '@/components/AppFilter/AppFilter'; @import '@/components/AppHeader/HeaderActions'; @@ -16,5 +20,6 @@ body[color-scheme='light'] { @import '@/components/AppSidePanel/AppSidePanel'; @import '@/components/AppStatementCard/AppStatementCard'; @import '@/components/AppToggleDarkMode/AppToggleDarkMode'; + @import '@/components/AppCommunity/AppLink'; @import '@/views/HomeView'; } diff --git a/src/assets/styles/main.scss b/src/assets/styles/main.scss index e14e895..3fdfe48 100644 --- a/src/assets/styles/main.scss +++ b/src/assets/styles/main.scss @@ -11,7 +11,8 @@ body { font-family: 'Ibarra Real Nova', serif; color: var(--color-dark-text); - input { + input, + button { &:disabled { @apply cursor-not-allowed; } @@ -20,6 +21,11 @@ body { @apply outline-amber-500; } } + button { + &:disabled { + @apply opacity-50; + } + } } .font { &--fira { diff --git a/src/components/AppAddUrl/AppAddUrl.vue b/src/components/AppAddUrl/AppAddUrl.vue index 0205cf3..9318aba 100644 --- a/src/components/AppAddUrl/AppAddUrl.vue +++ b/src/components/AppAddUrl/AppAddUrl.vue @@ -1,34 +1,78 @@ diff --git a/src/components/AppAddUrl/AppAddUrlError.vue b/src/components/AppAddUrl/AppAddUrlError.vue new file mode 100644 index 0000000..4aecc5d --- /dev/null +++ b/src/components/AppAddUrl/AppAddUrlError.vue @@ -0,0 +1,17 @@ + + + + + diff --git a/src/components/AppAddUrl/AppAddUrlPreview.vue b/src/components/AppAddUrl/AppAddUrlPreview.vue new file mode 100644 index 0000000..0cecf2c --- /dev/null +++ b/src/components/AppAddUrl/AppAddUrlPreview.vue @@ -0,0 +1,48 @@ + + + + + diff --git a/src/components/AppAddUrl/_AppAddUrl.dark.scss b/src/components/AppAddUrl/_AppAddUrl.dark.scss new file mode 100644 index 0000000..36f583c --- /dev/null +++ b/src/components/AppAddUrl/_AppAddUrl.dark.scss @@ -0,0 +1,5 @@ +.AppAddUrl { + input { + @apply bg-transparent; + } +} diff --git a/src/components/AppAddUrl/_AppAddUrl.scss b/src/components/AppAddUrl/_AppAddUrl.scss new file mode 100644 index 0000000..c53316e --- /dev/null +++ b/src/components/AppAddUrl/_AppAddUrl.scss @@ -0,0 +1,10 @@ +.AppAddUrl { + @apply flex mb-5 text-sm items-baseline; + input { + @apply p-3 rounded-lg border transition-all w-full; + max-height: 44px; + } + button { + @apply p-3 bg-black text-white mt-3 rounded-lg w-44; + } +} diff --git a/src/components/AppAddUrl/_AppAddUrlPreview.dark.scss b/src/components/AppAddUrl/_AppAddUrlPreview.dark.scss new file mode 100644 index 0000000..4c3edf2 --- /dev/null +++ b/src/components/AppAddUrl/_AppAddUrlPreview.dark.scss @@ -0,0 +1,9 @@ +.AppAddUrlPreview { + @apply bg-transparent; + &__content { + @apply text-gray-300; + &--desc { + @apply text-gray-500; + } + } +} diff --git a/src/components/AppAddUrl/_AppAddUrlPreview.scss b/src/components/AppAddUrl/_AppAddUrlPreview.scss new file mode 100644 index 0000000..eeecb1a --- /dev/null +++ b/src/components/AppAddUrl/_AppAddUrlPreview.scss @@ -0,0 +1,16 @@ +.AppAddUrlPreview { + @apply flex h-auto w-full bg-white border rounded-lg mb-4 transition-all overflow-hidden relative; + + &__img { + @apply min-w-[33%] w-1/3 rounded-l-lg bg-cover; + } + &__content { + @apply flex flex-col p-3 text-gray-800; + &--title { + @apply text-sm; + } + &--desc { + @apply text-xs text-gray-600 mt-2; + } + } +} diff --git a/src/components/AppCommunity/AppLink.vue b/src/components/AppCommunity/AppLink.vue index a94f802..d730d32 100644 --- a/src/components/AppCommunity/AppLink.vue +++ b/src/components/AppCommunity/AppLink.vue @@ -18,22 +18,5 @@ const formattedLink = computed(() => props.link.url.replace('https://', '')) diff --git a/src/components/AppCommunity/_AppLink.scss b/src/components/AppCommunity/_AppLink.scss new file mode 100644 index 0000000..496bfb0 --- /dev/null +++ b/src/components/AppCommunity/_AppLink.scss @@ -0,0 +1,21 @@ +.AppLink { + font-family: 'Fira Mono', serif; + @apply pl-3 pr-3 rounded-lg flex items-center; + background: linear-gradient( + 89.84deg, + rgba(230, 36, 174, 0.15) 0.34%, + rgba(94, 58, 255, 0.15) 16.96%, + rgba(10, 136, 255, 0.15) 34.66%, + rgba(75, 191, 80, 0.15) 50.12%, + rgba(137, 206, 0, 0.15) 66.22%, + rgba(239, 183, 0, 0.15) 82%, + rgba(246, 73, 0, 0.15) 99.9% + ); + border: 1px solid #d9e1ec; + &__icon { + @apply w-3 min-w-[0.9rem]; + } + &__url { + @apply text-xs truncate text-ellipsis ml-2; + } +} diff --git a/src/components/AppSidePanel/AppSidePanel.vue b/src/components/AppSidePanel/AppSidePanel.vue index 66b8990..8d93ffc 100644 --- a/src/components/AppSidePanel/AppSidePanel.vue +++ b/src/components/AppSidePanel/AppSidePanel.vue @@ -39,7 +39,7 @@ const closePanel = () => { Community Inspirations - + diff --git a/src/components/Icons/MdiCheckCircleOutline.vue b/src/components/Icons/MdiCheckCircleOutline.vue new file mode 100644 index 0000000..edd9165 --- /dev/null +++ b/src/components/Icons/MdiCheckCircleOutline.vue @@ -0,0 +1,8 @@ + diff --git a/src/composables/useValidateOgUrl.ts b/src/composables/useValidateOgUrl.ts new file mode 100644 index 0000000..beb5b84 --- /dev/null +++ b/src/composables/useValidateOgUrl.ts @@ -0,0 +1,31 @@ +import { type Ref, ref } from 'vue' + +export type OgItem = { + ogUrl?: string + requestUrl?: string + ogTitle?: string + ogDescription?: string + success?: boolean + ogImage?: { url: string }[] +} +export function useValidateOgUrl() { + const loading = ref(false) + const data: Ref<{ error: boolean; data: OgItem } | null> = ref(null) + const error = ref(null) + + const validate = (url: string) => { + loading.value = true + fetch(`${import.meta.env.VITE_APP_OPG_URL}?url=${url}`) + .then((res) => res.json()) + .then((json) => (data.value = json)) + .catch((err) => (error.value = err)) + .finally(() => (loading.value = false)) + } + + return { + validate, + loading, + data, + error + } +} diff --git a/src/graphql/mutations/samples.ts b/src/graphql/mutations/samples.ts new file mode 100644 index 0000000..1e0e6bb --- /dev/null +++ b/src/graphql/mutations/samples.ts @@ -0,0 +1,9 @@ +import gql from 'graphql-tag' +export const ADD_SAMPLE = gql` + mutation AddSample($objects: [samples_insert_input!]!) { + insert_samples(objects: $objects) { + __typename + affected_rows + } + } +` diff --git a/src/graphql/queries/categories.ts b/src/graphql/queries/categories.ts index c8af624..0fa97f0 100644 --- a/src/graphql/queries/categories.ts +++ b/src/graphql/queries/categories.ts @@ -3,6 +3,7 @@ import gql from 'graphql-tag' export const GET_CATEGORIES = gql` query GetCategories @cached { categories { + __typename id name emoji diff --git a/src/graphql/queries/statements.ts b/src/graphql/queries/statements.ts index 4810778..c39a5d2 100644 --- a/src/graphql/queries/statements.ts +++ b/src/graphql/queries/statements.ts @@ -3,14 +3,17 @@ import gql from 'graphql-tag' export const GET_STATEMENTS = gql` query GetStatements($where: statements_bool_exp) @cached { statements(where: $where) { + __typename id title description samples { + __typename id url } category { + __typename id name emoji diff --git a/src/types/index.d.ts b/src/types/index.d.ts index 9390a29..662d5b6 100644 --- a/src/types/index.d.ts +++ b/src/types/index.d.ts @@ -1,3 +1,2 @@ declare module 'click-outside-vue3' declare module 'lodash.shuffle' -declare module '@vue/apollo-composable' diff --git a/src/utils/util.ts b/src/utils/util.ts index c491062..371251a 100644 --- a/src/utils/util.ts +++ b/src/utils/util.ts @@ -5,3 +5,25 @@ export function randomChoice(arr: Array): T { export function truncate(text: string, MAX_COUNT = 178): string { return text.length > MAX_COUNT ? `${text.slice(0, MAX_COUNT)}...` : text } + +function getDomainName(url: string): string { + try { + const domain = /(?<=\.).+/.exec(new URL(url).hostname.replace('www.', '')) + if (domain) { + return domain.input + } + } catch (e) { + return '' + } + return '' +} +export function isValidUrl( + url: string, + allowedDomains: Array = ['figma.com', 'dribbble.com', 'behance.net', 'craftwork.design'] +): boolean { + const validUrlRegex = /^(https?|ftp):\/\/(www\.)?[^\s/$.?#].\S*$/i + url = !url.includes('http') && !url.includes('https') ? `https://${url}` : url + const domain = getDomainName(url) + + return validUrlRegex.test(url) && allowedDomains && allowedDomains.includes(domain) +} diff --git a/src/views/HomeView.vue b/src/views/HomeView.vue index 003a2a6..5583396 100644 --- a/src/views/HomeView.vue +++ b/src/views/HomeView.vue @@ -1,5 +1,5 @@