├── .gitignore ├── bookmark-manager ├── images │ └── favicon.ico ├── index.html ├── index.js └── style.css ├── budget-tracker ├── data.js ├── images │ └── favicon.ico ├── index.html ├── index.js └── style.css ├── kanban ├── data.js ├── images │ └── favicon.ico ├── index.html ├── index.js ├── kanban.js └── style.css ├── news-portal ├── data.js ├── images │ └── favicon.ico ├── index.html ├── index.js └── style.css ├── notNotion ├── budget-manager │ ├── data.js │ ├── images │ │ └── favicon.ico │ ├── index.html │ ├── index.js │ └── style.css ├── images │ ├── budget-manager.png │ ├── favicon.ico │ ├── kanban.png │ ├── logo.svg │ ├── news.png │ ├── productive.svg │ ├── quiz.png │ └── todo-list.png ├── index.html ├── main.js ├── master.css ├── progress-board │ ├── data.js │ ├── images │ │ └── favicon.ico │ ├── index.html │ ├── index.js │ ├── kanban.js │ └── style.css ├── quiz │ ├── images │ │ └── favicon.ico │ ├── index.html │ ├── index.js │ └── style.css └── todo-list │ ├── images │ └── favicon.ico │ ├── index.html │ ├── index.js │ └── style.css ├── quiz ├── images │ └── favicon.ico ├── index.html ├── index.js └── style.css └── task-manager ├── images └── favicon.ico ├── index.html ├── index.js └── style.css /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # IDEs and editors 33 | .idea 34 | .project 35 | .classpath 36 | .c9/ 37 | *.launch 38 | .settings/ 39 | *.sublime-workspace 40 | 41 | # IDE - VSCode 42 | .vscode/* 43 | !.vscode/settings.json 44 | !.vscode/tasks.json 45 | !.vscode/launch.json 46 | !.vscode/extensions.json 47 | 48 | # misc 49 | .sass-cache 50 | connect.lock 51 | typings 52 | 53 | # Logs 54 | logs 55 | *.log 56 | npm-debug.log* 57 | yarn-debug.log* 58 | yarn-error.log* 59 | 60 | 61 | # Dependency directories 62 | node_modules/ 63 | jspm_packages/ 64 | 65 | # Optional npm cache directory 66 | .npm 67 | 68 | # Optional eslint cache 69 | .eslintcache 70 | 71 | # Optional REPL history 72 | .node_repl_history 73 | 74 | # Output of 'npm pack' 75 | *.tgz 76 | 77 | # Yarn Integrity file 78 | .yarn-integrity 79 | 80 | # dotenv environment variables file 81 | .env 82 | 83 | # next.js build output 84 | .next 85 | 86 | # Lerna 87 | lerna-debug.log 88 | 89 | # System Files 90 | .DS_Store 91 | Thumbs.db -------------------------------------------------------------------------------- /bookmark-manager/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShubhamSarda/javascript-projects-ul/f83933e24b86ead70086a5a5ee7be5ce857a92c1/bookmark-manager/images/favicon.ico -------------------------------------------------------------------------------- /bookmark-manager/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | notNotion - Your Custom Bookmark Manager 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |
16 |
17 |
18 |

notNotion

19 |

Your Custom Bookmark Manager

20 |
21 |
22 |
23 | 24 | 25 | 31 | 32 |
33 |
34 |
35 |
36 | All 37 | Youtube 38 | Netflix 39 | Prime 40 | Other 41 |
42 |
43 | 44 |
45 |
46 |
47 |
48 |
49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /bookmark-manager/index.js: -------------------------------------------------------------------------------- 1 | import { initializeApp } from "https://www.gstatic.com/firebasejs/9.8.1/firebase-app.js"; 2 | import { getFirestore, collection, getDocs, addDoc, deleteDoc, doc, query, where, serverTimestamp } from "https://www.gstatic.com/firebasejs/9.8.1/firebase-firestore.js"; 3 | 4 | const firebaseConfig = { 5 | apiKey: "XXXXXXXXXXXXXXXXXX", 6 | authDomain: "XXXXXXXXXXXXXXXXXX", 7 | projectId: "XXXXXXXXXXXXXXXXXX", 8 | storageBucket: "XXXXXXXXXXXXXXXXXX", 9 | messagingSenderId: "XXXXXXXXXXXXXXXXXX", 10 | appId: "XXXXXXXXXXXXXXXXXX" 11 | }; 12 | 13 | 14 | const app = initializeApp(firebaseConfig); 15 | const db = getFirestore(); 16 | const colRef = collection(db, "bookmarks"); 17 | 18 | 19 | function deleteEvent(){ 20 | const deleteButtons = document.querySelectorAll("i.delete"); 21 | deleteButtons.forEach(button => { 22 | button.addEventListener("click", event => { 23 | const deleteRef = doc(db, "bookmarks", button.dataset.id); 24 | deleteDoc(deleteRef) 25 | .then(() => { 26 | button.parentElement.parentElement.parentElement.remove(); 27 | }) 28 | }) 29 | }); 30 | } 31 | 32 | 33 | function generateTemplate(response, id){ 34 | return `
35 |

${response.title}

36 |
37 |

38 | ${response.category[0].toUpperCase()}${response.category.slice(1)} 39 |

40 | 41 | 42 | 43 |
44 |
`; 45 | } 46 | 47 | 48 | const cards = document.querySelector(".cards"); 49 | function showCard(){ 50 | cards.innerHTML = ""; 51 | getDocs(colRef) 52 | .then(data => { 53 | data.docs.forEach(document => { 54 | cards.innerHTML += generateTemplate(document.data(), document.id); 55 | }); 56 | deleteEvent(); 57 | }) 58 | .catch(error => { 59 | console.log(error); 60 | }); 61 | } 62 | showCard(); 63 | 64 | 65 | const addForm = document.querySelector(".add"); 66 | addForm.addEventListener("submit", event => { 67 | event.preventDefault(); 68 | 69 | addDoc(colRef, { 70 | link: addForm.link.value, 71 | title: addForm.title.value, 72 | category: addForm.category.value, 73 | createdAt: serverTimestamp() 74 | }) 75 | .then(() => { 76 | addForm.reset(); 77 | showCard(); 78 | }) 79 | }); 80 | 81 | 82 | function filteredCards(category){ 83 | if(category === "All"){ 84 | showCard(); 85 | } else { 86 | const qRef = query(colRef, where("category", "==", category.toLowerCase())); 87 | cards.innerHTML = ""; 88 | getDocs(qRef) 89 | .then(data => { 90 | data.docs.forEach(document => { 91 | cards.innerHTML += generateTemplate(document.data(), document.id); 92 | }); 93 | deleteEvent(); 94 | }) 95 | .catch(error => { 96 | console.log(error); 97 | }); 98 | } 99 | } 100 | 101 | const categoryList = document.querySelector(".category-list"); 102 | const categorySpan = document.querySelectorAll(".category-list span"); 103 | categoryList.addEventListener("click", event => { 104 | if(event.target.tagName === "SPAN"){ 105 | filteredCards(event.target.innerText); 106 | categorySpan.forEach(span => span.classList.remove("active")); 107 | event.target.classList.add("active"); 108 | } 109 | }); -------------------------------------------------------------------------------- /bookmark-manager/style.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&family=Roboto&display=swap'); 2 | @import url("https://cdn.jsdelivr.net/npm/bootstrap-icons@1.8.2/font/bootstrap-icons.css"); 3 | 4 | :root{ 5 | --box-shadow: rgba(0, 0, 0, 0.16) 0px 1px 4px; 6 | } 7 | 8 | * { 9 | margin: 0; 10 | padding: 0; 11 | box-sizing: border-box; 12 | font-size: 16px; 13 | font-family: 'Poppins', sans-serif; 14 | text-decoration: none; 15 | list-style: none; 16 | } 17 | 18 | .outer-wrap{ 19 | width: 100%; 20 | min-height: 100vh; 21 | background-color: #F5F7FA; 22 | padding: 30px; 23 | } 24 | 25 | .inner-wrap{ 26 | max-width: 1200px; 27 | margin: auto; 28 | } 29 | 30 | .heading { 31 | margin: 30px auto; 32 | } 33 | 34 | .heading .title{ 35 | font-size: 30px; 36 | color: #000000; 37 | font-weight: 500; 38 | text-align: center; 39 | } 40 | 41 | .heading .subtitle{ 42 | font-size: 20px; 43 | color: #000000; 44 | font-weight: 300; 45 | text-align: center; 46 | } 47 | 48 | .create { 49 | background-color: #FFFFFF; 50 | padding: 20px; 51 | border-radius: 5px; 52 | box-shadow: var(--box-shadow); 53 | } 54 | 55 | .create .add { 56 | display: flex; 57 | flex-wrap: wrap; 58 | gap: 20px; 59 | } 60 | 61 | .create .add input#link{ 62 | flex-grow: 1; 63 | } 64 | 65 | .create form input, .create form select{ 66 | font-size: 18px; 67 | border-radius: 7px; 68 | border: 1px solid #D1D1D1; 69 | padding: 10px; 70 | } 71 | 72 | .create form option{ 73 | font-size: 18px; 74 | } 75 | 76 | .create form.add input#bookmark{ 77 | border: 0px; 78 | border-radius: 3px; 79 | background-color: #2666CF; 80 | color: #FFFFFF; 81 | cursor: pointer; 82 | padding: 10px 20px; 83 | } 84 | 85 | .category-list{ 86 | margin: 40px 0px; 87 | display: flex; 88 | justify-content: center; 89 | flex-wrap: wrap; 90 | } 91 | 92 | .category-list span{ 93 | padding: 10px; 94 | border-radius: 3px; 95 | background-color: #FFFFFF; 96 | box-shadow: var(--box-shadow); 97 | cursor: pointer; 98 | margin: 5px; 99 | } 100 | 101 | .category-list span:hover{ 102 | background-color: #EAEBEF; 103 | } 104 | 105 | .category-list .active{ 106 | background-color: #0D479a !important; 107 | color: #FFFFFF; 108 | } 109 | 110 | .cards{ 111 | display: flex; 112 | justify-content: space-evenly; 113 | flex-wrap: wrap; 114 | gap: 10px; 115 | margin: 25px 0px; 116 | } 117 | 118 | .card{ 119 | background-color: #FFFFFF; 120 | box-shadow: var(--box-shadow); 121 | padding: 10px; 122 | border-radius: 5px; 123 | width: 350px; 124 | margin: 7px 0px; 125 | } 126 | 127 | .card:hover{ 128 | background-color: #F7F7F7; 129 | } 130 | 131 | .card .sub-information{ 132 | display: flex; 133 | margin-top: 10px; 134 | } 135 | 136 | .card .sub-information p{ 137 | flex-grow: 1; 138 | } 139 | 140 | .card .sub-information .category{ 141 | background-color: #233E8B; 142 | padding: 2px 7px; 143 | border-radius: 3px; 144 | color: #FFFFFF; 145 | } 146 | 147 | .card .sub-information .category.netflix{ 148 | background-color: #b3010a; 149 | } 150 | 151 | .card .sub-information .category.youtube{ 152 | background-color: #4d4d4d; 153 | } 154 | 155 | .card .sub-information .category.prime{ 156 | background-color: #1468c2; 157 | } 158 | 159 | .card .sub-information .category.other{ 160 | background-color: #c47600; 161 | } 162 | 163 | .card .sub-information i{ 164 | margin: 7px; 165 | font-size: 18px; 166 | } 167 | 168 | .card .sub-information .website{ 169 | color: #393E46; 170 | } 171 | 172 | .card .sub-information .search{ 173 | color: #2666CF; 174 | } 175 | 176 | .card .sub-information .delete{ 177 | color: #EB596E; 178 | cursor: pointer; 179 | } 180 | 181 | @media (max-width: 800px){ 182 | .create .add{ 183 | flex-direction: column; 184 | } 185 | 186 | .card { 187 | flex-grow: 1; 188 | } 189 | } -------------------------------------------------------------------------------- /budget-tracker/data.js: -------------------------------------------------------------------------------- 1 | const transactions = [ 2 | {id: 123, source: "Freelancing", amount: 499, time: "4:40:38 AM 5/19/2022"}, 3 | {id: 124, source: "Studio", amount: -49, time: "5:40:38 AM 5/19/2022"}, 4 | {id: 125, source: "Cash", amount: 500, time: "7:40:38 AM 5/19/2022"}, 5 | {id: 126, source: "Recording", amount: 200, time: "9:40:38 AM 5/19/2022"}, 6 | ]; -------------------------------------------------------------------------------- /budget-tracker/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShubhamSarda/javascript-projects-ul/f83933e24b86ead70086a5a5ee7be5ce857a92c1/budget-tracker/images/favicon.ico -------------------------------------------------------------------------------- /budget-tracker/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | notNotion - Easiest Way To Track Your Transactions! 8 | 9 | 10 | 11 | 12 | 13 |
14 |
15 |
16 |
17 |

notNotion

18 |

Easiest Way To Track Your Transactions!

19 |
20 | 21 |
22 |
23 |

Add Transaction

24 |
25 | 26 | 27 | 28 |
29 |
30 |
31 |

Statistics

32 |

33 | Balance: $ 34 |

35 |

36 | Income: $ 37 |

38 |

39 | Expense: $ 40 |

41 |
42 |
43 | 44 |
45 |

Transaction History

46 |
47 |
48 |

Income

49 |
    50 | 51 |
52 |
53 |
54 |

Expense

55 |
    56 | 57 |
58 |
59 |
60 | 61 |
62 |
63 |
64 |
65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /budget-tracker/index.js: -------------------------------------------------------------------------------- 1 | const form = document.querySelector(".add"); 2 | const incomeList = document.querySelector("ul.income-list"); 3 | const expenseList = document.querySelector("ul.expense-list"); 4 | 5 | const balance = document.getElementById("balance"); 6 | const income = document.getElementById("income"); 7 | const expense = document.getElementById("expense"); 8 | 9 | let transactions = localStorage.getItem("transactions") !== null ? JSON.parse(localStorage.getItem("transactions")) : []; 10 | 11 | 12 | function updateStatistics(){ 13 | const updatedIncome = transactions 14 | .filter(transaction => transaction.amount > 0) 15 | .reduce((total, transaction) => total += transaction.amount, 0); 16 | 17 | const updatedExpense = transactions 18 | .filter(transaction => transaction.amount < 0) 19 | .reduce((total, transaction) => total += Math.abs(transaction.amount), 0); 20 | 21 | updatedBalance = updatedIncome - updatedExpense; 22 | balance.textContent = updatedBalance; 23 | income.textContent = updatedIncome; 24 | expense.textContent = updatedExpense; 25 | 26 | } 27 | 28 | function generateTemplate(id, source, amount, time){ 29 | return `
  • 30 |

    31 | ${source} 32 | ${time} 33 |

    34 | $${Math.abs(amount)} 35 | 36 |
  • `; 37 | } 38 | 39 | function addTransactionDOM(id, source, amount, time){ 40 | if(amount > 0){ 41 | incomeList.innerHTML += generateTemplate(id, source, amount, time); 42 | } else { 43 | expenseList.innerHTML += generateTemplate(id, source, amount, time); 44 | } 45 | } 46 | 47 | function addTransaction(source, amount){ 48 | const time = new Date(); 49 | const transaction = { 50 | id: Math.floor(Math.random()*100000), 51 | source: source, 52 | amount: amount, 53 | time: `${time.toLocaleTimeString()} ${time.toLocaleDateString()}` 54 | }; 55 | transactions.push(transaction); 56 | localStorage.setItem("transactions", JSON.stringify(transactions)); 57 | addTransactionDOM(transaction.id, source, amount, transaction.time); 58 | } 59 | 60 | form.addEventListener("submit", event => { 61 | event.preventDefault(); 62 | if(form.source.value.trim() === "" || form.amount.value === ""){ 63 | return alert("Please add proper values!"); 64 | } 65 | addTransaction(form.source.value.trim(), Number(form.amount.value)); 66 | updateStatistics(); 67 | form.reset(); 68 | }) 69 | 70 | function getTransaction(){ 71 | transactions.forEach(transaction => { 72 | if(transaction.amount > 0){ 73 | incomeList.innerHTML += generateTemplate(transaction.id, transaction.source, transaction.amount, transaction.time); 74 | } else { 75 | expenseList.innerHTML += generateTemplate(transaction.id, transaction.source, transaction.amount, transaction.time); 76 | } 77 | }); 78 | } 79 | 80 | function deleteTransaction(id){ 81 | transactions = transactions.filter(transaction => { 82 | return transaction.id !== id; 83 | }); 84 | localStorage.setItem("transactions", JSON.stringify(transactions)); 85 | } 86 | 87 | incomeList.addEventListener("click", event => { 88 | if(event.target.classList.contains("delete")){ 89 | event.target.parentElement.remove(); 90 | deleteTransaction(Number(event.target.parentElement.dataset.id)); 91 | updateStatistics(); 92 | } 93 | }); 94 | 95 | expenseList.addEventListener("click", event => { 96 | if(event.target.classList.contains("delete")){ 97 | event.target.parentElement.remove(); 98 | deleteTransaction(Number(event.target.parentElement.dataset.id)); 99 | updateStatistics(); 100 | } 101 | }); 102 | 103 | 104 | function init(){ 105 | updateStatistics(); 106 | getTransaction(); 107 | } 108 | 109 | init(); -------------------------------------------------------------------------------- /budget-tracker/style.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&family=Roboto&display=swap'); 2 | @import url("https://cdn.jsdelivr.net/npm/bootstrap-icons@1.8.2/font/bootstrap-icons.css"); 3 | 4 | :root{ 5 | --box-shadow: rgba(0, 0, 0, 0.16) 0px 1px 4px; 6 | } 7 | 8 | * { 9 | margin: 0; 10 | padding: 0; 11 | box-sizing: border-box; 12 | font-size: 16px; 13 | font-family: 'Poppins', sans-serif; 14 | text-decoration: none; 15 | list-style: none; 16 | } 17 | 18 | h3{ 19 | text-align: center; 20 | margin-bottom: 15px; 21 | text-decoration: underline solid #393E46 3px; 22 | font-size: 24px; 23 | } 24 | 25 | .outer-wrap{ 26 | width: 100%; 27 | min-height: 100vh; 28 | background-color: #F5F7FA; 29 | padding: 30px; 30 | } 31 | 32 | .inner-wrap{ 33 | max-width: 1200px; 34 | margin: auto; 35 | } 36 | 37 | .heading { 38 | margin: 30px auto; 39 | padding: 0px 20px; 40 | } 41 | 42 | .heading .title{ 43 | font-size: 30px; 44 | color: #000000; 45 | font-weight: 500; 46 | text-align: center; 47 | } 48 | 49 | .heading .subtitle{ 50 | font-size: 20px; 51 | color: #000000; 52 | font-weight: 300; 53 | text-align: center; 54 | } 55 | 56 | .transaction-control{ 57 | display: flex; 58 | justify-content: space-around; 59 | flex-wrap: wrap; 60 | gap: 10px; 61 | margin: 30px auto; 62 | } 63 | 64 | .actions, .stats{ 65 | flex-grow: 1; 66 | background-color: #FFFFFF; 67 | padding: 20px; 68 | box-shadow: var(--box-shadow); 69 | border-radius: 5px; 70 | margin: 0px 10px; 71 | } 72 | 73 | .actions .add{ 74 | display: flex; 75 | flex-direction: column; 76 | margin: 10px; 77 | } 78 | 79 | .actions .add input{ 80 | font-size: 18px; 81 | padding: 5px; 82 | margin: 10px; 83 | } 84 | 85 | .actions .add input[type="submit"]{ 86 | padding: 7px 15px; 87 | border-radius: 5px; 88 | border: 0px; 89 | background-color: #2666CF; 90 | color: #FFFFFF; 91 | cursor: pointer; 92 | box-shadow: var(--box-shadow); 93 | } 94 | 95 | .stats p{ 96 | background-color: #bed7ff; 97 | margin: 20px 10px; 98 | padding: 10px 20px; 99 | box-shadow: var(--box-shadow); 100 | font-size: 18px; 101 | border-radius: 2px; 102 | text-align: center; 103 | } 104 | 105 | .stats p.balance{ 106 | background-color: #fff2dd92; 107 | border-right: 10px solid #F6C065; 108 | } 109 | 110 | .stats p.income{ 111 | background-color: #e7fff1ad; 112 | border-right: 10px solid #16c77a; 113 | } 114 | 115 | .stats p.expense{ 116 | background-color: #ffeef0bb; 117 | border-right: 10px solid #EB596E; 118 | } 119 | 120 | .stats p span{ 121 | font-size: 18px; 122 | } 123 | 124 | .transaction-history{ 125 | margin: 40px 10px; 126 | background-color: #FFFFFF; 127 | border-radius: 10px; 128 | padding: 30px 0px; 129 | box-shadow: var(--box-shadow); 130 | } 131 | 132 | .records{ 133 | display: flex; 134 | justify-content: space-around; 135 | flex-wrap: wrap; 136 | gap: 20px; 137 | margin: 20px; 138 | } 139 | 140 | .records div{ 141 | flex-grow: 1; 142 | padding: 10px; 143 | } 144 | 145 | .records li{ 146 | display: flex; 147 | justify-content: space-between; 148 | background-color: #FFFFFF; 149 | padding: 10px; 150 | margin: 12px 0px; 151 | min-width: 250px; 152 | box-shadow: var(--box-shadow); 153 | font-size: 18px; 154 | } 155 | 156 | .records .income li{ 157 | border-left: 10px solid #2ECC71; 158 | } 159 | 160 | .records .expense li{ 161 | border-left: 10px solid #DF5E5E; 162 | } 163 | 164 | .records li p:first-child{ 165 | flex-grow: 1; 166 | display: flex; 167 | flex-direction: column; 168 | } 169 | 170 | .records span{ 171 | font-size: 18px; 172 | } 173 | 174 | .records span#time{ 175 | color: #717171; 176 | font-size: 12px; 177 | } 178 | 179 | .records i.delete{ 180 | margin-left: 15px; 181 | color: #082032; 182 | cursor: pointer; 183 | font-size: 18px; 184 | } 185 | 186 | .records i.delete:hover{ 187 | color: #B42B51; 188 | } 189 | 190 | -------------------------------------------------------------------------------- /kanban/data.js: -------------------------------------------------------------------------------- 1 | const data = [ 2 | { 3 | "columnId":0, 4 | "tasks":[ 5 | {"taskId":97522,"content":"Record JavaScript Intro"}, 6 | {"taskId":7243,"content":"Record Kanban Project"} 7 | ] 8 | }, 9 | { 10 | "columnId":1, 11 | "tasks":[ 12 | {"taskId":38833,"content":"System Design Notes"} 13 | ] 14 | }, 15 | { 16 | "columnId":2, 17 | "tasks":[ 18 | {"taskId":88198,"content":"React Projects"}, 19 | {"taskId":23030,"content":"Edit OOPS Lectures"} 20 | ] 21 | } 22 | ]; 23 | -------------------------------------------------------------------------------- /kanban/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShubhamSarda/javascript-projects-ul/f83933e24b86ead70086a5a5ee7be5ce857a92c1/kanban/images/favicon.ico -------------------------------------------------------------------------------- /kanban/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | notNotion - Web Based Kanban Style Application To Handle Project Progress 8 | 9 | 10 | 11 | 12 | 13 | 14 |
    15 |
    16 |
    17 |
    18 |

    notNotion

    19 |

    Web Based Kanban Style Application To Handle Project Progress

    20 |
    21 |
    22 |
    23 |
    24 |

    TODO

    25 | 2 26 |
    27 |
    28 |
    29 |
    30 | 31 | 32 |
    33 |
    34 |
    35 |
    36 |

    IN PROGRESS

    37 | 1 38 |
    39 |
    40 |
    41 |
    42 | 43 | 44 |
    45 |
    46 |
    47 |
    48 |

    COMPLETED

    49 | 2 50 |
    51 |
    52 |
    53 |
    54 | 55 | 56 |
    57 |
    58 |
    59 |
    60 |
    61 |
    62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /kanban/index.js: -------------------------------------------------------------------------------- 1 | import Kanban from "./kanban.js"; 2 | 3 | 4 | const todo = document.querySelector(".cards.todo"); 5 | const pending = document.querySelector(".cards.pending"); 6 | const completed = document.querySelector(".cards.completed"); 7 | const taskbox = [todo, pending, completed]; 8 | 9 | 10 | function addTaskCard(task, index){ 11 | const element = document.createElement("form"); 12 | element.className = "card"; 13 | element.draggable = true; 14 | element.dataset.id = task.taskId; 15 | element.innerHTML = ` 16 | 17 |
    18 | #${task.taskId} 19 | 20 | 21 | 22 | 23 | 24 |
    25 | `; 26 | taskbox[index].appendChild(element); 27 | } 28 | 29 | Kanban.getAllTasks().forEach((tasks, index) => { 30 | tasks.forEach(task => { 31 | addTaskCard(task, index); 32 | }) 33 | }); 34 | 35 | const addForm = document.querySelectorAll(".add"); 36 | addForm.forEach(form => { 37 | form.addEventListener("submit", event => { 38 | event.preventDefault(); 39 | if(form.task.value){ 40 | const task = Kanban.insertTask(form.submit.dataset.id, form.task.value.trim()); 41 | addTaskCard(task, form.submit.dataset.id); 42 | form.reset(); 43 | } 44 | }); 45 | }); 46 | 47 | taskbox.forEach(column => { 48 | column.addEventListener("click", event => { 49 | event.preventDefault(); 50 | 51 | const formInput = event.target.parentElement.parentElement.previousElementSibling; 52 | 53 | if(event.target.classList.contains("edit")){ 54 | formInput.removeAttribute("disabled"); 55 | event.target.classList.add("hide"); 56 | event.target.nextElementSibling.classList.remove("hide"); 57 | } 58 | 59 | if(event.target.classList.contains("update")){ 60 | formInput.setAttribute("disabled", "disabled"); 61 | event.target.classList.add("hide"); 62 | event.target.previousElementSibling.classList.remove("hide"); 63 | 64 | const taskId = event.target.dataset.id; 65 | const columnId = event.target.dataset.column; 66 | const content = formInput.value; 67 | Kanban.updateTask(taskId, { 68 | columnId: columnId, 69 | content: content 70 | }); 71 | } 72 | 73 | if(event.target.classList.contains("delete")){ 74 | formInput.parentElement.remove(); 75 | Kanban.deleteTask(event.target.dataset.id); 76 | } 77 | 78 | }); 79 | 80 | column.addEventListener("dragstart", event => { 81 | if(event.target.classList.contains("card")){ 82 | event.target.classList.add("dragging"); 83 | } 84 | }); 85 | 86 | column.addEventListener("dragover", event => { 87 | const card = document.querySelector(".dragging"); 88 | column.appendChild(card); 89 | }); 90 | 91 | column.addEventListener("dragend", event => { 92 | if(event.target.classList.contains("card")){ 93 | event.target.classList.remove("dragging"); 94 | 95 | const taskId = event.target.dataset.id; 96 | const columnId = event.target.parentElement.dataset.id; 97 | const content = event.target.task.value; 98 | Kanban.updateTask(taskId, { 99 | columnId: columnId, 100 | content: content 101 | }); 102 | } 103 | }); 104 | }); -------------------------------------------------------------------------------- /kanban/kanban.js: -------------------------------------------------------------------------------- 1 | export default class Kanban{ 2 | 3 | static getTasks(columnId){ 4 | const data = read().find(column => { 5 | return column.columnId == columnId; 6 | }); 7 | 8 | return data.tasks; 9 | } 10 | 11 | static insertTask(columnId, content){ 12 | const data = read(); 13 | const column = data.find(column => { 14 | return column.columnId == columnId; 15 | }); 16 | const task = { 17 | taskId: Math.floor(Math.random() * 100000), 18 | content: content 19 | }; 20 | 21 | column.tasks.push(task); 22 | console.log(data); 23 | save(data); 24 | 25 | return task; 26 | } 27 | 28 | static updateTask(taskId, updatedInformation){ 29 | const data = read(); 30 | 31 | function findColumnTask(){ 32 | for(const column of data){ 33 | const task = column.tasks.find(item => { 34 | return item.taskId == taskId; 35 | }); 36 | 37 | if(task){ 38 | return [task, column]; 39 | } 40 | } 41 | } 42 | const [task, currentColumn] = findColumnTask(); 43 | 44 | const targetColumn = data.find(column => { 45 | return column.columnId == updatedInformation.columnId; 46 | }); 47 | 48 | task.content = updatedInformation.content; 49 | currentColumn.tasks.splice(currentColumn.tasks.indexOf(task), 1); 50 | targetColumn.tasks.push(task); 51 | 52 | save(data); 53 | } 54 | 55 | static deleteTask(taskId){ 56 | const data = read(); 57 | 58 | for(const column of data){ 59 | const task = column.tasks.find(item => { 60 | return item.taskId == taskId; 61 | }); 62 | 63 | if(task){ 64 | column.tasks.splice(column.tasks.indexOf(task), 1); 65 | } 66 | } 67 | 68 | save(data); 69 | } 70 | 71 | static getAllTasks(){ 72 | const data = read(); 73 | columnCount(); 74 | return [data[0].tasks, data[1].tasks, data[2].tasks]; 75 | } 76 | } 77 | 78 | function read(){ 79 | const data = localStorage.getItem("data"); 80 | 81 | if(!data){ 82 | return [ 83 | {columnId: 0, tasks: []}, 84 | {columnId: 1, tasks: []}, 85 | {columnId: 2, tasks: []} 86 | ]; 87 | } 88 | 89 | return JSON.parse(data); 90 | } 91 | 92 | function save(data){ 93 | localStorage.setItem("data", JSON.stringify(data)); 94 | columnCount(); 95 | } 96 | 97 | function columnCount(){ 98 | const data = read(); 99 | 100 | const todo = document.querySelector("span.todo"); 101 | todo.textContent = data[0].tasks.length; 102 | 103 | const pending = document.querySelector("span.pending"); 104 | pending.textContent = data[1].tasks.length; 105 | 106 | const completed = document.querySelector("span.completed"); 107 | completed.textContent = data[2].tasks.length; 108 | } 109 | 110 | 111 | // console.log(Kanban.getAllTasks()); 112 | // console.log(Kanban.getTasks(1)); 113 | 114 | 115 | // console.log(Kanban.getTasks(1)); 116 | // console.log(Kanban.insertTask(0, "Record Kanban Lectures")); 117 | // console.log(Kanban.getTasks(1)); 118 | 119 | 120 | // console.log(Kanban.getAllTasks()); 121 | // Kanban.deleteTask(11822); 122 | // console.log(Kanban.getAllTasks()); 123 | 124 | 125 | // console.log(Kanban.getAllTasks()); 126 | // Kanban.updateTask(97522, { 127 | // columnId: 2, 128 | // content: "Record JavaScript Preview" 129 | // }); 130 | // console.log(Kanban.getAllTasks()); -------------------------------------------------------------------------------- /kanban/style.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&family=Roboto&display=swap'); 2 | @import url("https://cdn.jsdelivr.net/npm/bootstrap-icons@1.8.2/font/bootstrap-icons.css"); 3 | 4 | :root{ 5 | --box-shadow: rgba(0, 0, 0, 0.16) 0px 1px 4px; 6 | } 7 | 8 | * { 9 | margin: 0; 10 | padding: 0; 11 | box-sizing: border-box; 12 | font-size: 16px; 13 | font-family: 'Poppins', sans-serif; 14 | text-decoration: none; 15 | list-style: none; 16 | } 17 | 18 | .hide{ 19 | display: none; 20 | } 21 | 22 | .outer-wrap{ 23 | width: 100%; 24 | min-height: 100vh; 25 | background-color: #0D479A; 26 | padding: 30px; 27 | } 28 | 29 | .inner-wrap{ 30 | max-width: 1200px; 31 | margin: auto; 32 | } 33 | 34 | .heading{ 35 | margin-bottom: 30px; 36 | } 37 | 38 | .heading .title{ 39 | font-size: 30px; 40 | color: #FFFFFF; 41 | font-weight: 500; 42 | text-align: center; 43 | } 44 | 45 | .heading .subtitle{ 46 | font-size: 20px; 47 | color: #FFFFFF; 48 | font-weight: 300; 49 | text-align: center; 50 | } 51 | 52 | .kanban{ 53 | display: flex; 54 | justify-content: space-evenly; 55 | align-items: flex-start; 56 | min-height: 80vh; 57 | } 58 | 59 | .taskbox{ 60 | background-color: #D9DFE9; 61 | padding: 15px; 62 | border-radius: 5px; 63 | min-width: 325px; 64 | max-width: 500px; 65 | box-shadow: var(--box-shadow); 66 | margin: 10px; 67 | display: flex; 68 | flex-direction: column; 69 | } 70 | 71 | .taskbox .title{ 72 | display: flex; 73 | justify-content: space-between; 74 | align-items: center; 75 | padding: 10px 0px; 76 | } 77 | 78 | .taskbox .title span{ 79 | background-color: #E45826; 80 | color: #FFFFFF; 81 | padding: 10px; 82 | font-size: 16px; 83 | border-radius: 7px; 84 | } 85 | 86 | .card{ 87 | background-color: #FFFFFF; 88 | margin: 10px 0px; 89 | padding: 10px; 90 | border-radius: 5px; 91 | display: flex; 92 | flex-direction: column; 93 | box-shadow: var(--box-shadow); 94 | } 95 | 96 | .card input{ 97 | background-color: #FFFFFF; 98 | margin: 10px 0px; 99 | padding: 10px; 100 | border-radius: 5px; 101 | box-shadow: rgba(99, 99, 99, 0.2) 0px 2px 8px 0px; 102 | border: 0px; 103 | } 104 | 105 | .card div{ 106 | display: flex; 107 | justify-content: space-between; 108 | } 109 | 110 | .card div span{ 111 | font-size: 12px; 112 | padding: 5px 0px; 113 | align-self: center; 114 | 115 | } 116 | 117 | .card div span.task-id{ 118 | background-color: #002366; 119 | padding: 5px; 120 | color: #FFFFFF; 121 | border-radius: 5px; 122 | } 123 | 124 | .card button{ 125 | color: #FFFFFF; 126 | font-size: 14px; 127 | padding: 8px; 128 | border-radius: 5px; 129 | cursor: pointer; 130 | margin-left: 1px; 131 | border: 0px; 132 | box-shadow: rgba(0, 0, 0, 0.18) 0px 2px 4px; 133 | } 134 | 135 | .card .edit{ 136 | background-color: #646464; 137 | } 138 | 139 | .card .update{ 140 | background-color: #005ed8; 141 | } 142 | 143 | .card .delete{ 144 | background-color: #e20000; 145 | } 146 | 147 | .card.dragging{ 148 | opacity: 0.4; 149 | } 150 | 151 | .add{ 152 | display: flex; 153 | } 154 | 155 | .add input{ 156 | flex-grow: 1; 157 | padding: 10px; 158 | border-radius: 5px; 159 | border: 0px; 160 | box-shadow: rgba(0, 0, 0, 0.18) 0px 2px 4px; 161 | font-size: 16px; 162 | } 163 | 164 | .add button{ 165 | background-color: #50B286; 166 | color: #FFFFFF; 167 | font-size: 20px; 168 | padding: 10px; 169 | border: 0px; 170 | border-radius: 5px; 171 | box-shadow: rgba(0, 0, 0, 0.18) 0px 2px 4px; 172 | cursor: pointer; 173 | margin-left: 4px; 174 | } 175 | 176 | @media (max-width: 1080px){ 177 | .kanban{ 178 | flex-direction: column; 179 | align-items: center; 180 | } 181 | } -------------------------------------------------------------------------------- /news-portal/data.js: -------------------------------------------------------------------------------- 1 | { 2 | "status": "ok", 3 | "totalResults": 23090, 4 | "articles": [ 5 | { 6 | "source": { 7 | "id": null, 8 | "name": "This is Money" 9 | }, 10 | "author": "Ray Massey", 11 | "title": "A highly charged debate: Is Government's electric car plan realistic?", 12 | "description": "Are you ready for 'charging rage'? That's what experts predict will happen when too many electric car drivers compete for too few public charging points.", 13 | "url": "https://www.thisismoney.co.uk/money/cars/article-10834553/A-highly-charged-debate-Governments-electric-car-plan-realistic.html", 14 | "urlToImage": "https://i.dailymail.co.uk/1s/2022/05/19/20/58038675-0-image-a-17_1652989635157.jpg", 15 | "publishedAt": "2022-05-19T20:50:46Z", 16 | "content": "Are you ready for charging rage? Thats what experts predict will happen when too many electric car drivers compete for too few public charging points.\r\nPrepare for squabbles at the wave of bespoke el… [+8906 chars]" 17 | }, 18 | { 19 | "source": { 20 | "id": null, 21 | "name": "Naftemporiki.gr" 22 | }, 23 | "author": null, 24 | "title": "Wall Street: Απώλειες με τον S&P 500 ακόμη πιο κοντά σε bear market", 25 | "description": "Μετά από μία έντονα νευρική συνεδρίαση με αρκετές αυξομειώσεις, ο S&P 500 πλησίασε ακόμη πιο κοντά τη bear market, με τους επενδυτές να συνεχίζουν να ρευστοποιούν μετοχές υπό τον φόβο ότι οι επιτοκιακές αυξήσεις από τη Fed θα οδηγήσουν την αμερικανική οικ…", 26 | "url": "https://www.naftemporiki.gr/finance/story/1864605/wall-street-apoleies-me-ton-skaip-500-akomi-pio-konta-se-bear-market", 27 | "urlToImage": "https://www.naftemporiki.gr/fu/t/1864605/1080/568/0x0000000001d278c0/2/1/wall-street-apoleies-skaip-akomi-konta-bear-market.jpg", 28 | "publishedAt": "2022-05-19T20:38:07Z", 29 | "content": ", S&P 500 bear market, Fed . \r\n 0,58%, 4% , 18% . \r\nDow Jones 0,75%, 2020 , 1.164 . Nasdaq 0,26%, 4,7% .\r\n Cisco 14%. Cisco .  \r\n , S&P 500 Nasdaq . Synopsys 12%, , cloud Datadog 12%. «»  Nvi… [+213 chars]" 30 | }, 31 | { 32 | "source": { 33 | "id": null, 34 | "name": "Challenges" 35 | }, 36 | "author": "Reuters", 37 | "title": "Wall Street finit en baisse, dans le sillage de Cisco et Apple", 38 | "description": "par Devik Jain et Noel Randewich\n(Reuters) - La Bourse de New York a fini en ordre dispersé jeudi à l'issue d'une séance en dents de scie, Cisco Systems ayant plongé après avoir revu à la baisse ses prévisions, tandis que les investisseurs sont restés préoccu…", 39 | "url": "https://www.challenges.fr/finance-et-marche/wall-street-finit-en-baisse-dans-le-sillage-de-cisco-et-apple_813923", 40 | "urlToImage": "https://www.challenges.fr/assets/img/2022/05/19/cover-r4x3w1000-6286ab3b16949-wall-street-finit-en-baisse-dans-le-sillage-de-cisco-et.jpg", 41 | "publishedAt": "2022-05-19T20:30:43Z", 42 | "content": "par Devik Jain et Noel Randewich\r\n(Reuters) - La Bourse de New York a fini en ordre dispersé jeudi à l'issue d'une séance en dents de scie, Cisco Systems ayant plongé après avoir revu à la baisse ses… [+2228 chars]" 43 | }, 44 | { 45 | "source": { 46 | "id": null, 47 | "name": "Billings Gazette" 48 | }, 49 | "author": "By Brian Fung, hhttps://www.cnn.com/business", 50 | "title": "Twitter will now label and suppress misinformation surrounding armed conflict and other crises", 51 | "description": "The platform's new crisis misinformation policy is designed to slow the spread of viral falsehoods during natural disasters, armed conflict and public health emergencies.", 52 | "url": "https://billingsgazette.com/news/national/twitter-will-now-label-and-suppress-misinformation-surrounding-armed-conflict-and-other-crises/article_9e26c4b1-0270-5883-82b2-c92d11aa0870.html", 53 | "urlToImage": "https://bloximages.chicago2.vip.townnews.com/billingsgazette.com/content/tncms/assets/v3/editorial/9/e2/9e26c4b1-0270-5883-82b2-c92d11aa0870/628682336bf65.preview.jpg?crop=1763%2C926%2C0%2C124&resize=1200%2C630&order=crop%2Cresize", 54 | "publishedAt": "2022-05-19T20:30:00Z", 55 | "content": "Twitter will now label and suppress misinformation surrounding armed conflict and other crises.\r\nAdobe Stock\r\nTwitter will now apply warning labels to and cease recommending claims that outside exper… [+13284 chars]" 56 | }, 57 | { 58 | "source": { 59 | "id": null, 60 | "name": "Overclockers.ru" 61 | }, 62 | "author": "amv212", 63 | "title": "Первый электромобиль Bentley будет иметь мощность 1400 л.с. и разгоняться до 100 км в час за 1,5 секунды", 64 | "description": "Британский производитель автомобилей класса люкс Bentley выходит на рынок электромобилей со экстремальным разгоном от 0 до 100 км/ч за 1,5 сек. Но вы можете перейти на более спокойный режим с разгоном за 2,7 сек.", 65 | "url": "https://overclockers.ru/blog/amv212/show/67472/pervyj-elektromobil-bentley-budet-imet-moschnost-1400-l-s-i-razgonyatsya-do-100-km-v-chas-za-1-5-sekundy", 66 | "urlToImage": "https://overclockers.ru/st/legacy/blog/396911/303965_O.jpg", 67 | "publishedAt": "2022-05-19T20:28:14Z", 68 | "content": "Bentley - , , , EV .\r\n Bentley. Wuka/iStock\r\n Bentley \"Beyond100\", 2030 BEV, , 2025 .\r\n Bentley , , 2025 , 1 400 (1 044 ) 0-60 / (0-100 /) 1,5 . , , Bentley Lucid Air Dream Edition Performance (1 111… [+450 chars]" 69 | }, 70 | { 71 | "source": { 72 | "id": null, 73 | "name": "Forbes" 74 | }, 75 | "author": "Russell Flannery, Forbes Staff, \n Russell Flannery, Forbes Staff\n https://www.forbes.com/sites/russellflannery/", 76 | "title": "Xiaomi Posts 1st Quarter Loss As Covid, Parts Shortage Hurt Smartphone Sales", 77 | "description": "Xiaomi is the world’s No. 3 smartphone brands after Samsung and Apple", 78 | "url": "https://www.forbes.com/sites/russellflannery/2022/05/19/xiaomi-posts-1st-quarter-loss-as-covid-parts-shortage-hurt-smartphone-sales/", 79 | "urlToImage": "https://imageio.forbes.com/specials-images/imageserve//6286a3671fe239c4265b113f/0x0.jpg?format=jpg&width=1200", 80 | "publishedAt": "2022-05-19T20:27:22Z", 81 | "content": "A shopper walks past a Xiaomi store in Shanghai last year. Retail business this year has been hurt ... [+] by Covid lockdowns. Photographer: Qilai Shen/Bloomberg\r\n© 2021 Bloomberg Finance LP\r\nXiaomi,… [+3207 chars]" 82 | }, 83 | { 84 | "source": { 85 | "id": null, 86 | "name": "Terra.com.br" 87 | }, 88 | "author": "Estadão Conteúdo", 89 | "title": "Musk perde US$ 74,5 bilhões desde anúncio de compra do Twitter", 90 | "description": "Homem mais rico do mundo vem encolhendo patrimônio para financiar a operação, enquanto Tesla é criticada pelo mercado financeiro", 91 | "url": "https://www.terra.com.br/noticias/tecnologia/musk-perde-us-745-bilhoes-desde-anuncio-de-compra-do-twitter,47c24a5efeab565daf21b155dc3ff08ez8ub3bye.html", 92 | "urlToImage": "https://p2.trrsf.com/image/fget/cf/1200/900/middle/images.terra.com/2022/05/18/738089836-484419584766c0690c268k-e1612805327597-1060x596.jpg", 93 | "publishedAt": "2022-05-19T20:27:06Z", 94 | "content": "Homem mais rico do mundo, o empresário Elon Musk perdeu cerca de US$ 74,5 bilhões desde 13 de abril, quando o bilionário anunciou a aquisição do Twitter, segundo a revista Forbes. O negócio deve sair… [+1430 chars]" 95 | }, 96 | { 97 | "source": { 98 | "id": null, 99 | "name": "Washington Free Beacon" 100 | }, 101 | "author": "Chuck Ross, Chuck Ross", 102 | "title": "Meet the Conspiracy Theorist Behind Twitter’s ‘Crisis Misinformation Policy’", 103 | "description": "Twitter’s pick to stop the spread of misinformation in times of crisis has a history of pushing falsehoods.\nThe post Meet the Conspiracy Theorist Behind Twitter’s ‘Crisis Misinformation Policy’ appeared first on Washington Free Beacon.", 104 | "url": "https://freebeacon.com/?p=1596594", 105 | "urlToImage": "https://freebeacon.com/wp-content/uploads/2021/11/WAyOm6Xk_400x400_736x514.jpg", 106 | "publishedAt": "2022-05-19T20:25:28Z", 107 | "content": "Twitters pick to stop the spread of misinformation in times of crisis has a history of pushing falsehoods.\r\nYoel Roth, the head of Twitters safety and integrity unit, unveiled the sites \"crisis misin… [+1955 chars]" 108 | }, 109 | { 110 | "source": { 111 | "id": "marca", 112 | "name": "Marca" 113 | }, 114 | "author": "LW", 115 | "title": "Elon Musk and Twitter's legal team contradict each other on 'stalled deal'", 116 | "description": "Elon Musk recently stated that the Twitter deal was on hold, he revealed that he encountered several issues with how many bots and fake accounts the platform has. At the same time,", 117 | "url": "https://www.marca.com/en/lifestyle/us-news/2022/05/19/6286a740e2704e9a2b8b45a8.html", 118 | "urlToImage": "https://phantom-marca.unidadeditorial.es/7fad84f7fb8d7c95e3c820594ea80b10/resize/1200/f/jpg/assets/multimedia/imagenes/2022/05/19/16529906732561.jpg", 119 | "publishedAt": "2022-05-19T20:24:00Z", 120 | "content": "Elon Musk recently stated that the Twitter deal was on hold, he revealed that he encountered several issues with how many bots and fake accounts the platform has. At the same time, former president D… [+1835 chars]" 121 | }, 122 | { 123 | "source": { 124 | "id": null, 125 | "name": "Ixbt.com" 126 | }, 127 | "author": "mpak@ixbt.com (MPAK)", 128 | "title": "Samsung придумала замену магнитикам на холодильник, но это будет недёшево. Новый сервис позволит напечатать любое изображение на панелях линейки Bespoke", 129 | "description": "Компания Samsung решила заработать на тех, кто очень хочет сделать свой холодильник уникальным.   \r\n Корейский гигант в конце текущего года запустит услугу, которая позволит всем владельцам холодильников серии Bespoke напечатать на съёмных панелях любое изобр…", 130 | "url": "https://www.ixbt.com/news/2022/05/19/samsung-pridumala-zamenu-magnitikam-na-holodilnik-no-jeto-budet-nedjoshevo-novyj-servis-pozvolit-napechatat-ljuboe.html", 131 | "urlToImage": "https://www.ixbt.com/img/n1/news/2022/4/4/Samsung_Bespoke_Olive_Mannella.0_large.jpg", 132 | "publishedAt": "2022-05-19T20:22:00Z", 133 | "content": "Samsung , .  \r\n , Bespoke , .  \r\n, . , - . \r\n 250 , 500 . — Samsung. \r\nMeta ( ) «Meta Pay»\r\n