├── public └── favicon.ico ├── .vscode └── extensions.json ├── src ├── directives │ └── vAutofocus.js ├── assets │ ├── logo.svg │ ├── main.css │ └── base.css ├── main.js ├── composables │ └── useCharactersLimit.js ├── App.vue ├── js │ └── firebase.js ├── router │ └── index.js ├── Views │ ├── StatsView.vue │ ├── EditNoteView.vue │ ├── NotesView.vue │ └── AuthView.vue ├── components │ ├── Notes │ │ ├── AddEditNote.vue │ │ ├── DeleteNoteModal.vue │ │ └── SingleNote.vue │ └── Layout │ │ └── NavBar.vue └── stores │ └── NotesStore.js ├── vite.config.js ├── .gitignore ├── index.html ├── package.json └── README.md /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leelanarasimha/noteslist-vue/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"] 3 | } 4 | -------------------------------------------------------------------------------- /src/directives/vAutofocus.js: -------------------------------------------------------------------------------- 1 | export const vAutofocus = { 2 | mounted(el) { 3 | el.focus(); 4 | } 5 | }; 6 | -------------------------------------------------------------------------------- /src/assets/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue'; 2 | import App from './App.vue'; 3 | import router from './router/index'; 4 | import { createPinia } from 'pinia'; 5 | 6 | const app = createApp(App); 7 | 8 | const pinia = createPinia(); 9 | app.use(router); 10 | app.use(pinia); 11 | app.mount('#app'); 12 | -------------------------------------------------------------------------------- /src/composables/useCharactersLimit.js: -------------------------------------------------------------------------------- 1 | import { watch } from 'vue'; 2 | 3 | export function useCharactersLimit(value, limit = 100) { 4 | watch(value, (newValue, oldValue) => { 5 | if (newValue.length === limit) { 6 | alert(`Sorry!! more than ${limit} characters are not allowed`); 7 | } 8 | }); 9 | } 10 | -------------------------------------------------------------------------------- /vite.config.js: -------------------------------------------------------------------------------- 1 | import { fileURLToPath, URL } from 'node:url' 2 | 3 | import { defineConfig } from 'vite' 4 | import vue from '@vitejs/plugin-vue' 5 | 6 | // https://vitejs.dev/config/ 7 | export default defineConfig({ 8 | plugins: [vue()], 9 | resolve: { 10 | alias: { 11 | '@': fileURLToPath(new URL('./src', import.meta.url)) 12 | } 13 | } 14 | }) 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | .DS_Store 12 | dist 13 | dist-ssr 14 | coverage 15 | *.local 16 | 17 | /cypress/videos/ 18 | /cypress/screenshots/ 19 | 20 | # Editor directories and files 21 | .vscode/* 22 | !.vscode/extensions.json 23 | .idea 24 | *.suo 25 | *.ntvs* 26 | *.njsproj 27 | *.sln 28 | *.sw? 29 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite App 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 19 | 20 | 23 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "noteslist-vue", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "vite", 7 | "build": "vite build", 8 | "preview": "vite preview" 9 | }, 10 | "dependencies": { 11 | "@vueuse/core": "^10.2.0", 12 | "bulma": "^0.9.4", 13 | "firebase": "^9.23.0", 14 | "pinia": "^2.1.3", 15 | "vue": "^3.3.2", 16 | "vue-router": "^4.2.2" 17 | }, 18 | "devDependencies": { 19 | "@vitejs/plugin-vue": "^4.2.3", 20 | "vite": "^4.3.5" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/assets/main.css: -------------------------------------------------------------------------------- 1 | @import './base.css'; 2 | 3 | #app { 4 | max-width: 1280px; 5 | margin: 0 auto; 6 | padding: 2rem; 7 | 8 | font-weight: normal; 9 | } 10 | 11 | a, 12 | .green { 13 | text-decoration: none; 14 | color: hsla(160, 100%, 37%, 1); 15 | transition: 0.4s; 16 | } 17 | 18 | @media (hover: hover) { 19 | a:hover { 20 | background-color: hsla(160, 100%, 37%, 0.2); 21 | } 22 | } 23 | 24 | @media (min-width: 1024px) { 25 | body { 26 | display: flex; 27 | place-items: center; 28 | } 29 | 30 | #app { 31 | display: grid; 32 | grid-template-columns: 1fr 1fr; 33 | padding: 0 2rem; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # noteslist-vue 2 | 3 | This template should help get you started developing with Vue 3 in Vite. 4 | 5 | ## Recommended IDE Setup 6 | 7 | [VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin). 8 | 9 | ## Customize configuration 10 | 11 | See [Vite Configuration Reference](https://vitejs.dev/config/). 12 | 13 | ## Project Setup 14 | 15 | ```sh 16 | npm install 17 | ``` 18 | 19 | ### Compile and Hot-Reload for Development 20 | 21 | ```sh 22 | npm run dev 23 | ``` 24 | 25 | ### Compile and Minify for Production 26 | 27 | ```sh 28 | npm run build 29 | ``` 30 | -------------------------------------------------------------------------------- /src/js/firebase.js: -------------------------------------------------------------------------------- 1 | // Import the functions you need from the SDKs you need 2 | import { initializeApp } from 'firebase/app'; 3 | import { getFirestore } from 'firebase/firestore'; 4 | // TODO: Add SDKs for Firebase products that you want to use 5 | // https://firebase.google.com/docs/web/setup#available-libraries 6 | 7 | // Your web app's Firebase configuration 8 | const firebaseConfig = { 9 | apiKey: 'AIzaSyCgCsSurdcSSGp1Tuik8L9GqOE65eenISU', 10 | authDomain: 'noteslist-vue.firebaseapp.com', 11 | projectId: 'noteslist-vue', 12 | storageBucket: 'noteslist-vue.appspot.com', 13 | messagingSenderId: '692628756704', 14 | appId: '1:692628756704:web:70ccaed79e28eea4ec1885' 15 | }; 16 | 17 | // Initialize Firebase 18 | const app = initializeApp(firebaseConfig); 19 | 20 | const db = getFirestore(app); 21 | 22 | export { db }; 23 | -------------------------------------------------------------------------------- /src/router/index.js: -------------------------------------------------------------------------------- 1 | import { createRouter, createWebHashHistory } from 'vue-router'; 2 | 3 | import NotesView from '../Views/NotesView.vue'; 4 | import StatsView from '../Views/StatsView.vue'; 5 | import EditNoteView from '../Views/EditNoteView.vue'; 6 | import AuthView from '../Views/AuthView.vue'; 7 | 8 | const routes = [ 9 | { 10 | path: '/', 11 | component: NotesView, 12 | name: 'notes' 13 | }, 14 | { 15 | path: '/edit-note/:id', 16 | component: EditNoteView, 17 | name: 'editNote' 18 | }, 19 | { 20 | path: '/stats', 21 | component: StatsView, 22 | name: 'stats' 23 | }, 24 | { 25 | path: '/auth', 26 | component: AuthView, 27 | name: 'auth' 28 | } 29 | ]; 30 | 31 | const router = createRouter({ 32 | history: createWebHashHistory(), 33 | routes 34 | }); 35 | 36 | export default router; 37 | -------------------------------------------------------------------------------- /src/Views/StatsView.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 41 | -------------------------------------------------------------------------------- /src/Views/EditNoteView.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 38 | -------------------------------------------------------------------------------- /src/Views/NotesView.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 48 | -------------------------------------------------------------------------------- /src/components/Notes/AddEditNote.vue: -------------------------------------------------------------------------------- 1 | 25 | 26 | 59 | -------------------------------------------------------------------------------- /src/Views/AuthView.vue: -------------------------------------------------------------------------------- 1 | 39 | 40 | 45 | 46 | 52 | -------------------------------------------------------------------------------- /src/components/Layout/NavBar.vue: -------------------------------------------------------------------------------- 1 | 38 | 39 | 44 | 45 | 54 | -------------------------------------------------------------------------------- /src/components/Notes/DeleteNoteModal.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 66 | -------------------------------------------------------------------------------- /src/components/Notes/SingleNote.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 60 | -------------------------------------------------------------------------------- /src/assets/base.css: -------------------------------------------------------------------------------- 1 | /* color palette from */ 2 | :root { 3 | --vt-c-white: #ffffff; 4 | --vt-c-white-soft: #f8f8f8; 5 | --vt-c-white-mute: #f2f2f2; 6 | 7 | --vt-c-black: #181818; 8 | --vt-c-black-soft: #222222; 9 | --vt-c-black-mute: #282828; 10 | 11 | --vt-c-indigo: #2c3e50; 12 | 13 | --vt-c-divider-light-1: rgba(60, 60, 60, 0.29); 14 | --vt-c-divider-light-2: rgba(60, 60, 60, 0.12); 15 | --vt-c-divider-dark-1: rgba(84, 84, 84, 0.65); 16 | --vt-c-divider-dark-2: rgba(84, 84, 84, 0.48); 17 | 18 | --vt-c-text-light-1: var(--vt-c-indigo); 19 | --vt-c-text-light-2: rgba(60, 60, 60, 0.66); 20 | --vt-c-text-dark-1: var(--vt-c-white); 21 | --vt-c-text-dark-2: rgba(235, 235, 235, 0.64); 22 | } 23 | 24 | /* semantic color variables for this project */ 25 | :root { 26 | --color-background: var(--vt-c-white); 27 | --color-background-soft: var(--vt-c-white-soft); 28 | --color-background-mute: var(--vt-c-white-mute); 29 | 30 | --color-border: var(--vt-c-divider-light-2); 31 | --color-border-hover: var(--vt-c-divider-light-1); 32 | 33 | --color-heading: var(--vt-c-text-light-1); 34 | --color-text: var(--vt-c-text-light-1); 35 | 36 | --section-gap: 160px; 37 | } 38 | 39 | @media (prefers-color-scheme: dark) { 40 | :root { 41 | --color-background: var(--vt-c-black); 42 | --color-background-soft: var(--vt-c-black-soft); 43 | --color-background-mute: var(--vt-c-black-mute); 44 | 45 | --color-border: var(--vt-c-divider-dark-2); 46 | --color-border-hover: var(--vt-c-divider-dark-1); 47 | 48 | --color-heading: var(--vt-c-text-dark-1); 49 | --color-text: var(--vt-c-text-dark-2); 50 | } 51 | } 52 | 53 | *, 54 | *::before, 55 | *::after { 56 | box-sizing: border-box; 57 | margin: 0; 58 | font-weight: normal; 59 | } 60 | 61 | body { 62 | min-height: 100vh; 63 | color: var(--color-text); 64 | background: var(--color-background); 65 | transition: color 0.5s, background-color 0.5s; 66 | line-height: 1.6; 67 | font-family: Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, 68 | Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; 69 | font-size: 15px; 70 | text-rendering: optimizeLegibility; 71 | -webkit-font-smoothing: antialiased; 72 | -moz-osx-font-smoothing: grayscale; 73 | } 74 | -------------------------------------------------------------------------------- /src/stores/NotesStore.js: -------------------------------------------------------------------------------- 1 | import { defineStore } from 'pinia'; 2 | import { computed, ref } from 'vue'; 3 | import { 4 | getDocs, 5 | collection, 6 | onSnapshot, 7 | setDoc, 8 | doc, 9 | updateDoc, 10 | deleteDoc, 11 | addDoc, 12 | query, 13 | orderBy, 14 | limit 15 | } from 'firebase/firestore'; 16 | import { db } from '../js/firebase'; 17 | 18 | export const useNotesStore = defineStore('notesStore', () => { 19 | const notes = ref([]); 20 | const notesLoaded = ref(false); 21 | 22 | const notesCollectionRef = collection(db, 'notes'); 23 | const notesCollectionQuery = query(notesCollectionRef, orderBy('date', 'desc')); 24 | 25 | const getNoteContentById = computed(() => { 26 | return (id) => { 27 | return notes.value.find((note) => note.id === id).content; 28 | }; 29 | }); 30 | 31 | const getNotes = async () => { 32 | // const querySnapshot = await getDocs(collection(db, 'notes')); 33 | // querySnapshot.forEach((doc) => { 34 | // let note = { 35 | // id: doc.id, 36 | // content: doc.data().content 37 | // }; 38 | // notes.value.push(note); 39 | // }); 40 | 41 | onSnapshot(notesCollectionQuery, (querySnapshot) => { 42 | let notesData = []; 43 | notesLoaded.value = false; 44 | querySnapshot.forEach((doc) => { 45 | let note = { 46 | id: doc.id, 47 | content: doc.data().content, 48 | date: doc.data().date 49 | }; 50 | notesData.push(note); 51 | }); 52 | 53 | notes.value = notesData; 54 | notesLoaded.value = true; 55 | }); 56 | }; 57 | 58 | const totalNotesCount = computed(() => { 59 | return notes.value.length; 60 | }); 61 | 62 | const totalCharactersCount = computed(() => { 63 | let count = 0; 64 | for (let note of notes.value) { 65 | count += note.content.length; 66 | } 67 | return count; 68 | }); 69 | 70 | const addNote = async (noteContent) => { 71 | const currentDate = new Date().getTime().toString(); 72 | // const note = { 73 | // id: currentDate, 74 | // content: noteContent 75 | // }; 76 | 77 | //notes.value.unshift(note); 78 | 79 | await addDoc(notesCollectionRef, { content: noteContent, date: currentDate }); 80 | }; 81 | 82 | const deleteNote = async (noteId) => { 83 | await deleteDoc(doc(notesCollectionRef, noteId)); 84 | }; 85 | 86 | const updateNote = async (id, content) => { 87 | await updateDoc(doc(notesCollectionRef, id), { 88 | content 89 | }); 90 | }; 91 | 92 | return { 93 | notes, 94 | updateNote, 95 | getNoteContentById, 96 | addNote, 97 | deleteNote, 98 | totalCharactersCount, 99 | totalNotesCount, 100 | getNotes, 101 | notesLoaded 102 | }; 103 | }); 104 | --------------------------------------------------------------------------------