├── .editorconfig ├── .gitignore ├── .prettierrc ├── @types ├── plugin.d.ts └── vue-shim.d.ts ├── README.md ├── api └── index.ts ├── assets ├── README.md ├── empty-bookmarks.svg └── variables.scss ├── components ├── about.vue ├── appbar.vue ├── bookmark.vue ├── cards.vue ├── navigation.vue ├── notification.vue └── spinner.vue ├── layouts ├── default.vue ├── error.vue └── search.vue ├── nuxt.config.js ├── package-lock.json ├── package.json ├── pages ├── bookmarks.vue ├── index.vue └── search.vue ├── plugins └── notifier.ts ├── static └── favicon.ico ├── store ├── index.ts └── notification.ts └── tsconfig.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Node template 3 | # Logs 4 | /logs 5 | *.log 6 | npm-debug.log* 7 | yarn-debug.log* 8 | yarn-error.log* 9 | 10 | # Runtime data 11 | pids 12 | *.pid 13 | *.seed 14 | *.pid.lock 15 | 16 | # Directory for instrumented libs generated by jscoverage/JSCover 17 | lib-cov 18 | 19 | # Coverage directory used by tools like istanbul 20 | coverage 21 | 22 | # nyc test coverage 23 | .nyc_output 24 | 25 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 26 | .grunt 27 | 28 | # Bower dependency directory (https://bower.io/) 29 | bower_components 30 | 31 | # node-waf configuration 32 | .lock-wscript 33 | 34 | # Compiled binary addons (https://nodejs.org/api/addons.html) 35 | build/Release 36 | 37 | # Dependency directories 38 | node_modules/ 39 | jspm_packages/ 40 | 41 | # TypeScript v1 declaration files 42 | typings/ 43 | 44 | # Optional npm cache directory 45 | .npm 46 | 47 | # Optional eslint cache 48 | .eslintcache 49 | 50 | # Optional REPL history 51 | .node_repl_history 52 | 53 | # Output of 'npm pack' 54 | *.tgz 55 | 56 | # Yarn Integrity file 57 | .yarn-integrity 58 | 59 | # dotenv environment variables file 60 | .env 61 | 62 | # parcel-bundler cache (https://parceljs.org/) 63 | .cache 64 | 65 | # next.js build output 66 | .next 67 | 68 | # nuxt.js build output 69 | .nuxt 70 | 71 | # Nuxt generate 72 | dist 73 | 74 | # vuepress build output 75 | .vuepress/dist 76 | 77 | # Serverless directories 78 | .serverless 79 | 80 | # IDE / Editor 81 | .idea 82 | 83 | # Service worker 84 | sw.* 85 | 86 | # macOS 87 | .DS_Store 88 | 89 | # Vim swap files 90 | *.swp 91 | 92 | .vercel 93 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "singleQuote": true 4 | } 5 | -------------------------------------------------------------------------------- /@types/plugin.d.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | declare module 'vue/types/vue' { 4 | interface Vue { 5 | $notify: (message: string) => void 6 | } 7 | } 8 | 9 | declare module 'vuex/types/index' { 10 | interface Store { 11 | $notify(message: string): void 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /@types/vue-shim.d.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import { Framework } from 'vuetify' 3 | import { Context, Middleware } from '@nuxt/types' 4 | 5 | declare module '*.vue' { 6 | import Vue from 'vue' 7 | export default Vue 8 | } 9 | 10 | declare module 'vue/types/options' { 11 | interface ComponentOptions { 12 | fetch?(ctx: Context): Promise | void 13 | layout?: string | ((ctx: Context) => string) 14 | middleware?: Middleware | Middleware[] 15 | } 16 | } 17 | 18 | declare module 'vue/types/vue' { 19 | interface Vue { 20 | $vuetify: Framework 21 | } 22 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # :fire: The Index 2 | 3 | A curated list of awesome design resources for web developers 4 | 5 | # ✨ Features 6 | 7 | :heart: **Lightweight and minimalistic design**: Highly minimal and simple design 8 | 9 | :rainbow: **Dark Mode**: Tend to your eyes in the darkness 10 | 11 | :zap: **Easy to use**: Simple and intuitive design thats easy to use 12 | 13 | :bookmark: **Bookmarks**: Bookmark your favourite resources easily. 14 | 15 | :mag: **Search**: Looking for a particular resource? Just search for it using the search button. 16 | 17 | :rocket: **Performance**: The Index is built using Vuejs, HTML5, and CSS3 which makes it lightning-fast and gives a great performance. 18 | 19 | --- 20 | 21 | ## 🧰 Built with 22 | 23 | - VueJS 24 | - Vuetify 25 | - HTML5 26 | - CSS3 27 | 28 | ## :construction_worker: Setup 29 | 30 | - Clone the Repository. 31 | 32 | ``` 33 | $ git clone https://github.com/sumitkolhe/theindex 34 | ``` 35 | 36 | - Install the dependencies for the backend 37 | 38 | ``` 39 | $ cd theindex 40 | 41 | $ npm install 42 | ``` 43 | 44 | - Compiles and hot-reloads for development 45 | 46 | ``` 47 | $ npm run serve 48 | ``` 49 | 50 | - Compiles and minifies for production 51 | 52 | ``` 53 | $ npm run build 54 | ``` 55 | 56 | - Lints and fixes files 57 | 58 | ``` 59 | $ npm run lint 60 | ``` 61 | 62 | ## ✍️ Authors 63 | 64 | - [**Sumit Kolhe**](https://github.com/sumitkolhe) - _Author_ 65 | 66 | ## 📜 License 67 | 68 | This project is licensed under the [MIT License](https://opensource.org/licenses/MIT) - see the [LICENSE](LICENSE) file for details. 69 | -------------------------------------------------------------------------------- /api/index.ts: -------------------------------------------------------------------------------- 1 | import { NowRequest, NowResponse } from '@vercel/node' 2 | import axios from 'axios' 3 | import cheerio from 'cheerio' 4 | import showdown from 'showdown' 5 | 6 | const converter = new showdown.Converter() 7 | converter.setFlavor('github') 8 | 9 | module.exports = (req: NowRequest, res: NowResponse) => { 10 | res.setHeader('Access-Control-Allow-Origin', '*') 11 | res.setHeader('Access-Control-Allow-Headers', '*') 12 | res.setHeader('Access-Control-Allow-Methods', 'GET, OPTIONS') 13 | 14 | axios 15 | .get( 16 | 'https://raw.githubusercontent.com/bradtraversy/design-resources-for-developers/master/readme.md' 17 | ) 18 | .then((response) => { 19 | const converted_html = converter.makeHtml(response.data) 20 | const $ = cheerio.load(converted_html) 21 | 22 | let category_name: string 23 | let resources: Array = [] 24 | let websites: Array = [] 25 | let categories: Array = [] 26 | 27 | for (let iter = 0; iter < 29; iter++) { 28 | $('body') 29 | .children('h2') 30 | .eq(iter + 1) 31 | .each((i, elem) => { 32 | category_name = $(elem).text() 33 | }) 34 | 35 | $('body') 36 | .children('h2') 37 | .eq(iter + 1) 38 | .each((i, elem) => { 39 | categories.push($(elem).text()) 40 | }) 41 | 42 | $('body') 43 | .children('table') 44 | .eq(iter) 45 | .children('tbody') 46 | .children('tr') 47 | .each((i, elem) => { 48 | resources[i] = { 49 | title: $(elem).children('td:first-child').text(), 50 | description: $(elem).children('td:nth-child(2)').text(), 51 | link: $(elem) 52 | .children('td:first-child') 53 | .children('a') 54 | .attr('href'), 55 | logo: 56 | 'https://logo.clearbit.com/' + 57 | $(elem).children('td:first-child').children('a').attr('href'), 58 | category: category_name, 59 | } 60 | }) 61 | 62 | websites[iter] = resources 63 | resources = [] 64 | } 65 | 66 | res.json({ categories, websites }) 67 | }) 68 | .catch(() => { 69 | res.json({ message: 'Something went wrong' }) 70 | }) 71 | } 72 | -------------------------------------------------------------------------------- /assets/README.md: -------------------------------------------------------------------------------- 1 | # ASSETS 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | This directory contains your un-compiled assets such as LESS, SASS, or JavaScript. 6 | 7 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/assets#webpacked). 8 | -------------------------------------------------------------------------------- /assets/empty-bookmarks.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /assets/variables.scss: -------------------------------------------------------------------------------- 1 | // Ref: https://github.com/nuxt-community/vuetify-module#customvariables 2 | // 3 | // The variables you want to modify 4 | // $font-size-root: 20px; 5 | -------------------------------------------------------------------------------- /components/about.vue: -------------------------------------------------------------------------------- 1 | 44 | 45 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /components/appbar.vue: -------------------------------------------------------------------------------- 1 | 70 | 71 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /components/bookmark.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 75 | 76 | -------------------------------------------------------------------------------- /components/cards.vue: -------------------------------------------------------------------------------- 1 | 56 | 57 | 74 | 75 | -------------------------------------------------------------------------------- /components/navigation.vue: -------------------------------------------------------------------------------- 1 | 68 | 69 | 111 | 112 | 129 | -------------------------------------------------------------------------------- /components/notification.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | -------------------------------------------------------------------------------- /components/spinner.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 9 | 10 | -------------------------------------------------------------------------------- /layouts/default.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 17 | 18 | 23 | -------------------------------------------------------------------------------- /layouts/error.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 39 | 40 | 45 | -------------------------------------------------------------------------------- /layouts/search.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 17 | 18 | 23 | -------------------------------------------------------------------------------- /nuxt.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | target: 'static', 3 | ssr: false, 4 | head: { 5 | titleTemplate: '%s - Design Resources for Developers', 6 | title: 'Resources', 7 | meta: [ 8 | { charset: 'utf-8' }, 9 | { name: 'viewport', content: 'width=device-width, initial-scale=1' }, 10 | { hid: 'description', name: 'description', content: '' }, 11 | ], 12 | link: [ 13 | { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }, 14 | { 15 | rel: 'stylesheet', 16 | type: 'text/css', 17 | href: 'https://fonts.googleapis.com/css2?family=Montserrat:wght@200;300;400;500;600;700;800;900', 18 | }, 19 | ], 20 | }, 21 | 22 | components: true, 23 | 24 | buildModules: ['@nuxt/typescript-build', '@nuxtjs/vuetify'], 25 | 26 | modules: ['@nuxtjs/axios', '@nuxtjs/pwa'], 27 | 28 | plugins: ['~/plugins/notifier.ts'], 29 | 30 | axios: {}, 31 | 32 | pwa: { 33 | manifest: { 34 | lang: 'en', 35 | }, 36 | }, 37 | 38 | vuetify: { 39 | theme: { 40 | options: {}, 41 | dark: false, 42 | default: false, 43 | disable: false, 44 | dark: true, 45 | themes: { 46 | dark: { 47 | primary: '#ffffff', 48 | secondary: '#613dc1', 49 | accent: '#ff5050', 50 | surface: '#242424', 51 | background: '#121212', 52 | }, 53 | light: { 54 | primary: '#111111', 55 | secondary: '#00bbf9', 56 | accent: '#ff5050', 57 | surface: '#fafafa', 58 | background: '#fff', 59 | }, 60 | }, 61 | }, 62 | }, 63 | 64 | build: { 65 | extractCSS: true, 66 | }, 67 | } 68 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "the-index", 3 | "version": "2.0.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "nuxt", 7 | "build": "nuxt generate", 8 | "start": "nuxt start", 9 | "vercel": "vercel dev --listen 80", 10 | "deploy": "vercel deploy --prod" 11 | }, 12 | "dependencies": { 13 | "@nuxtjs/axios": "^5.12.5", 14 | "@nuxtjs/pwa": "^3.3.5", 15 | "axios": "^0.21.1", 16 | "cheerio": "^1.0.0-rc.5", 17 | "core-js": "^3.8.3", 18 | "nuxt": "^2.14.12", 19 | "showdown": "^1.9.1" 20 | }, 21 | "devDependencies": { 22 | "@nuxt/types": "^2.14.12", 23 | "@nuxt/typescript-build": "^2.0.4", 24 | "@nuxtjs/vuetify": "^1.11.3", 25 | "@types/showdown": "^1.9.3", 26 | "@vercel/node": "^1.9.0", 27 | "eslint-config-prettier": "^7.2.0", 28 | "eslint-plugin-prettier": "^3.3.1", 29 | "prettier": "^2.2.1" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /pages/bookmarks.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 50 | -------------------------------------------------------------------------------- /pages/index.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 45 | -------------------------------------------------------------------------------- /pages/search.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 56 | -------------------------------------------------------------------------------- /plugins/notifier.ts: -------------------------------------------------------------------------------- 1 | export default ({ store }: any, inject: any) => { 2 | inject('notify', { 3 | success(message: string) { 4 | store.commit('notification/showNotification', { 5 | message: message, 6 | color: 'success', 7 | }) 8 | }, 9 | error(message: string) { 10 | store.commit('notification/showNotification', { 11 | message: message, 12 | color: 'error', 13 | }) 14 | }, 15 | info(message: string) { 16 | store.commit('notification/showNotification', { 17 | message: message, 18 | color: 'success', 19 | }) 20 | }, 21 | }) 22 | } 23 | -------------------------------------------------------------------------------- /static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sumitkolhe/design-resources/831f313494938bdb726a070a57a1f77662381ec7/static/favicon.ico -------------------------------------------------------------------------------- /store/index.ts: -------------------------------------------------------------------------------- 1 | import { MutationTree, GetterTree, ActionTree } from 'vuex' 2 | 3 | export type WebsitesState = ReturnType 4 | 5 | export const state = () => ({ 6 | all_websites: [], 7 | categories: [], 8 | bookmarked_websites: [], 9 | filtered_websites: [], 10 | website_group: Array(), 11 | category: 0, 12 | loading: true, 13 | }) 14 | 15 | export const mutations: MutationTree = { 16 | SET_ALL_WEBSITES: (state, websites) => { 17 | state.all_websites = websites 18 | }, 19 | SET_WEBSITE_GROUP: (state, index) => { 20 | state.website_group = state.all_websites[index] 21 | }, 22 | SET_BOOKMARKED_WEBSITES: (state, websites) => { 23 | state.bookmarked_websites = websites 24 | }, 25 | SET_FILTERED_WEBSITES: (state, search_term) => { 26 | state.filtered_websites = [] 27 | state.all_websites.forEach((website_group: any) => 28 | website_group.forEach((website: any) => { 29 | if ( 30 | website.title.includes(search_term) || 31 | website.description.includes(search_term) 32 | ) { 33 | state.filtered_websites.push(website) 34 | } 35 | }) 36 | ) 37 | }, 38 | SET_CATEGORIES: (state, categories) => { 39 | state.categories = categories 40 | }, 41 | SET_CATEGORY: (state, index) => { 42 | state.category = index 43 | }, 44 | SET_LOADING: (state, loading) => { 45 | state.loading = loading 46 | }, 47 | } 48 | 49 | export const actions: ActionTree = { 50 | async fetchData({ commit }) { 51 | const data = await this.$axios.$get('https://resources.sumit.co/api') 52 | commit('SET_CATEGORIES', data.categories) 53 | commit('SET_ALL_WEBSITES', data.websites) 54 | commit('SET_WEBSITE_GROUP', 0) 55 | }, 56 | async fetchBookmarkedWebsites({ commit }) { 57 | const data = (await JSON.parse(localStorage.getItem('bookmarks')!)) || [] 58 | let bookmark_categories: string[] = [] 59 | data.forEach((website: any) => { 60 | if (bookmark_categories.indexOf(website.category) === -1) 61 | bookmark_categories.push(website.category) 62 | }) 63 | commit('SET_CATEGORIES', bookmark_categories) 64 | commit('SET_BOOKMARKED_WEBSITES', data) 65 | }, 66 | } 67 | 68 | export const getters: GetterTree = { 69 | GET_ALL_WEBSITES: (state) => { 70 | return state.all_websites 71 | }, 72 | GET_WEBSITE_GROUP: (state) => { 73 | return state.website_group 74 | }, 75 | GET_FILTERED_WEBSITES: (state) => { 76 | return state.filtered_websites 77 | }, 78 | GET_BOOKMARKED_WEBSITES: (state) => { 79 | return state.bookmarked_websites 80 | }, 81 | GET_CATEGORIES: (state) => { 82 | return state.categories 83 | }, 84 | GET_LOADING: (state) => { 85 | return state.loading 86 | }, 87 | } 88 | -------------------------------------------------------------------------------- /store/notification.ts: -------------------------------------------------------------------------------- 1 | import { MutationTree } from 'vuex' 2 | 3 | export type NotificationState = ReturnType 4 | 5 | export const state = () => ({ 6 | message: '', 7 | color: '', 8 | }) 9 | 10 | export const mutations: MutationTree = { 11 | showNotification(state, payload) { 12 | state.message = payload.message 13 | state.color = payload.color 14 | }, 15 | } 16 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2018", 4 | "module": "ESNext", 5 | "moduleResolution": "Node", 6 | "lib": ["ESNext", "ESNext.AsyncIterable", "DOM"], 7 | "esModuleInterop": true, 8 | "allowJs": true, 9 | "sourceMap": true, 10 | "strict": true, 11 | "noEmit": true, 12 | "experimentalDecorators": true, 13 | "baseUrl": ".", 14 | "paths": { 15 | "~/*": ["./*"], 16 | "@/*": ["./*"] 17 | }, 18 | "types": ["@nuxt/types", "@nuxtjs/axios", "@types/node"] 19 | }, 20 | "include": ["**/*", "@types"], 21 | "exclude": ["node_modules", ".nuxt", "dist"] 22 | } 23 | --------------------------------------------------------------------------------