├── .env ├── layouts ├── index.ts └── PageLayout │ ├── index.ts │ ├── pageLayout.module.css │ └── PagesLayout.tsx ├── .eslintrc.json ├── components ├── Date │ ├── date.module.css │ ├── index.ts │ └── date.tsx ├── Menu │ ├── index.ts │ ├── menu.module.css │ └── Menu.tsx ├── Footer │ ├── index.ts │ ├── Footer.module.css │ └── Footer.tsx ├── Header │ ├── index.ts │ └── Header.tsx ├── ContentEditor │ ├── index.ts │ └── ContentEditor.tsx ├── index.ts └── Form │ ├── Form.jsx │ └── CommentForm.jsx ├── data ├── main.json ├── infrastructure │ ├── Auth0_Docs.md │ ├── Vercel-Doc.md │ └── GitHub_Actions.md ├── soft-skills │ ├── Pomodoro_Technique.md │ ├── junior-dev-resources.md │ └── jedi-technics.md ├── test-works │ └── my-first-test-work.md └── programming │ ├── React_TypeScript_Cheatsheets.md │ ├── Shell_scripting_with_NodeJS.md │ └── Tackling_TypeScript.md ├── README.md ├── next.config.js ├── pages ├── _app.tsx ├── api │ ├── hello.ts │ └── comments │ │ ├── index.js │ │ └── [id] │ │ └── index.js ├── index.tsx ├── comments │ ├── [pageid] │ │ ├── edit.js │ │ └── index.jsx │ └── index.jsx ├── test-works │ ├── [pageid].tsx │ └── index.tsx ├── soft-skills │ ├── [pageid].tsx │ └── index.tsx ├── programming │ ├── [pageid].tsx │ └── index.tsx └── infrastructure │ ├── [pageid].tsx │ └── index.tsx ├── lib ├── getAllFilesIds.ts ├── parseMarkdownFile.ts └── dbConnect.js ├── tsconfig.json ├── models └── Comments.js ├── .gitignore ├── e2e └── example.spec.ts ├── .github └── workflows │ ├── playwright.yml │ └── build.yml ├── package.json ├── LICENSE ├── playwright.config.ts └── tests-examples └── demo-todo-app.spec.ts /.env: -------------------------------------------------------------------------------- 1 | MONGODB_URI= 2 | -------------------------------------------------------------------------------- /layouts/index.ts: -------------------------------------------------------------------------------- 1 | export { PagesLayout } from './PageLayout' 2 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /components/Date/date.module.css: -------------------------------------------------------------------------------- 1 | .day {} 2 | 3 | .month {} 4 | 5 | .year {} 6 | -------------------------------------------------------------------------------- /components/Date/index.ts: -------------------------------------------------------------------------------- 1 | import Date from './date' 2 | 3 | export { Date } 4 | -------------------------------------------------------------------------------- /components/Menu/index.ts: -------------------------------------------------------------------------------- 1 | import Menu from './Menu' 2 | 3 | export { Menu } 4 | -------------------------------------------------------------------------------- /components/Footer/index.ts: -------------------------------------------------------------------------------- 1 | import Footer from './Footer' 2 | 3 | export { Footer } 4 | -------------------------------------------------------------------------------- /components/Header/index.ts: -------------------------------------------------------------------------------- 1 | import Header from './Header' 2 | 3 | export { Header } 4 | -------------------------------------------------------------------------------- /layouts/PageLayout/index.ts: -------------------------------------------------------------------------------- 1 | import PagesLayout from './PagesLayout' 2 | 3 | export { PagesLayout } 4 | -------------------------------------------------------------------------------- /components/Footer/Footer.module.css: -------------------------------------------------------------------------------- 1 | .footer { 2 | display: flex; 3 | } 4 | 5 | .footer__item { 6 | 7 | } 8 | -------------------------------------------------------------------------------- /data/main.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "My personal learning path", 3 | "title": "My personal learning path" 4 | } 5 | -------------------------------------------------------------------------------- /components/ContentEditor/index.ts: -------------------------------------------------------------------------------- 1 | import ContentEditor from './ContentEditor' 2 | 3 | export { 4 | ContentEditor 5 | } 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # personal-learn-path 2 | Learn path constructor and knowledge base 3 | 4 | ## Tech stack 5 | - NextJS 6 | - MongoDB 7 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | reactStrictMode: true, 4 | swcMinify: true, 5 | } 6 | 7 | module.exports = nextConfig 8 | -------------------------------------------------------------------------------- /components/Header/Header.tsx: -------------------------------------------------------------------------------- 1 | import { Menu } from '../index' 2 | 3 | export default function Header () { 4 | return ( 5 |
6 | 7 |
8 | ) 9 | } 10 | -------------------------------------------------------------------------------- /data/infrastructure/Auth0_Docs.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'Auth0 Docs' 3 | date: '2022-10-11' 4 | --- 5 | 6 | Auth0 is an easy to implement, adaptable authentication and authorization platform. 7 | 8 | https://auth0.com/docs/ 9 | -------------------------------------------------------------------------------- /pages/_app.tsx: -------------------------------------------------------------------------------- 1 | import 'antd/dist/reset.css' 2 | import type { AppProps } from 'next/app' 3 | 4 | function MyApp({ Component, pageProps }: AppProps) { 5 | return 6 | } 7 | 8 | export default MyApp 9 | -------------------------------------------------------------------------------- /components/index.ts: -------------------------------------------------------------------------------- 1 | import { Date } from './Date' 2 | import { Header } from './Header' 3 | import { Menu } from './Menu' 4 | import { Footer } from './Footer' 5 | import { ContentEditor } from './ContentEditor' 6 | 7 | export { 8 | Date, 9 | Header, 10 | Menu, 11 | Footer, 12 | ContentEditor, 13 | } 14 | -------------------------------------------------------------------------------- /lib/getAllFilesIds.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs' 2 | 3 | /** 4 | * @param {String} path 5 | * @param {String} [ext='md'] 6 | * @returns {String[]} 7 | */ 8 | export default function getAllFilesIds (path: fs.PathLike, ext = 'md') { 9 | const fileNames = fs.readdirSync(path) 10 | 11 | return fileNames.map(name => name.replace(/\.md$/, '')) 12 | } 13 | -------------------------------------------------------------------------------- /data/soft-skills/Pomodoro_Technique.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'The Pomodoro® Technique' 3 | date: '2022-10-11' 4 | --- 5 | 6 | The Pomodoro® Technique is an easy and fun way to get the most out of time management. Turn time into a valuable ally to accomplish tasks while keeping track of your progress. 7 | 8 | https://francescocirillo.com/products/the-pomodoro-technique 9 | -------------------------------------------------------------------------------- /pages/api/hello.ts: -------------------------------------------------------------------------------- 1 | // Next.js API route support: https://nextjs.org/docs/api-routes/introduction 2 | import type { NextApiRequest, NextApiResponse } from 'next' 3 | 4 | type Data = { 5 | name: string 6 | } 7 | 8 | export default function handler( 9 | req: NextApiRequest, 10 | res: NextApiResponse 11 | ) { 12 | res.status(200).json({ name: 'John Doe' }) 13 | } 14 | -------------------------------------------------------------------------------- /layouts/PageLayout/pageLayout.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | max-width: 36rem; 3 | padding: 0 1rem; 4 | margin: 3rem auto 6rem; 5 | } 6 | 7 | .header { 8 | display: flex; 9 | flex-direction: column; 10 | align-items: center; 11 | } 12 | 13 | .headerImage { 14 | width: 6rem; 15 | } 16 | 17 | .headerHomeImage { 18 | width: 6rem; 19 | } 20 | 21 | .backToHome { 22 | margin: 3rem 0 0; 23 | } 24 | -------------------------------------------------------------------------------- /data/soft-skills/junior-dev-resources.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: '7 resources that made me the junior developer I am today' 3 | date: '2022-10-11' 4 | --- 5 | 6 | 💨 The FAST method to learn anything 7 | 🔁 Spaced repetition 8 | 🤝🏻 My accountability partner 9 | 👾 Scrimba Discord community 10 | 📚 Tech Resume Inside Out 11 | 🍅 The pomodoro technique 12 | 🤔 Embracing stoicism 13 | 14 | https://scrimba.com/articles/junior-dev-resources/ 15 | -------------------------------------------------------------------------------- /data/infrastructure/Vercel-Doc.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'Vercel' 3 | date: '2022-10-11' 4 | --- 5 | 6 | Vercel is the platform for frontend developers, providing the speed and reliability innovators need to create at the moment of inspiration. 7 | 8 | We enable teams to iterate quickly and develop, preview, and ship delightful user experiences. Vercel has zero-configuration support for 35+ frontend frameworks and integrates with your headless content, commerce, or database of choice. 9 | 10 | Start the tutorial or deploy a template in minutes. 11 | -------------------------------------------------------------------------------- /components/Menu/menu.module.css: -------------------------------------------------------------------------------- 1 | .root { 2 | display: flex; 3 | } 4 | 5 | @media (max-width: 500px) { 6 | .root { 7 | flex-direction: column; 8 | margin: 0 auto; 9 | max-width: 50vw; 10 | } 11 | } 12 | 13 | .item { 14 | flex-grow: 1; 15 | text-align: center; 16 | text-decoration: underline 3px #4183c4; 17 | } 18 | 19 | .item_active { 20 | color: #999999; 21 | text-decoration: none; 22 | } 23 | 24 | .item:last-child { 25 | margin-right: 0; 26 | } 27 | 28 | @media (max-width: 500px) { 29 | .item { 30 | margin: 0 0 .5em 0; 31 | text-align: left; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "forceConsistentCasingInFileNames": true, 9 | "noEmit": true, 10 | "esModuleInterop": true, 11 | "module": "esnext", 12 | "moduleResolution": "node", 13 | "resolveJsonModule": true, 14 | "isolatedModules": true, 15 | "jsx": "preserve", 16 | "incremental": true 17 | }, 18 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], 19 | "exclude": ["node_modules"] 20 | } 21 | -------------------------------------------------------------------------------- /models/Comments.js: -------------------------------------------------------------------------------- 1 | import mongoose from 'mongoose' 2 | 3 | const CommentSchema = new mongoose.Schema({ 4 | _id: mongoose.Types.ObjectId, 5 | name: { 6 | type: String, 7 | required: [true, "Please provide commenter name"], 8 | }, 9 | email: { 10 | type: String, 11 | required: [true, 'Please specify the email of commenter.'], 12 | }, 13 | movie_id: { 14 | type: mongoose.Types.ObjectId, 15 | }, 16 | text: { 17 | type: String, 18 | }, 19 | date: { 20 | type: Date, 21 | }, 22 | }) 23 | 24 | export default mongoose.models.Comment || mongoose.model('Comment', CommentSchema) 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # editors 4 | .idea 5 | 6 | # dependencies 7 | node_modules 8 | /.pnp 9 | .pnp.js 10 | 11 | # testing 12 | /coverage 13 | 14 | # next.js 15 | /.next/ 16 | /out/ 17 | 18 | # production 19 | /build 20 | 21 | # misc 22 | .DS_Store 23 | *.pem 24 | 25 | # debug 26 | npm-debug.log* 27 | yarn-debug.log* 28 | yarn-error.log* 29 | .pnpm-debug.log* 30 | 31 | # local env files 32 | .env*.local 33 | 34 | # vercel 35 | .vercel 36 | 37 | # typescript 38 | *.tsbuildinfo 39 | next-env.d.ts 40 | /test-results/ 41 | /playwright-report/ 42 | /playwright/.cache/ 43 | -------------------------------------------------------------------------------- /components/Date/date.tsx: -------------------------------------------------------------------------------- 1 | import { parseISO, format } from 'date-fns' 2 | import { ru } from 'date-fns/locale' 3 | import dateStyles from './date.module.css' 4 | 5 | type DatePropsType = { 6 | dateString: string 7 | } 8 | 9 | export default function Date({ dateString }: DatePropsType) { 10 | const date = parseISO(dateString) 11 | return ( 12 | 17 | ) 18 | } 19 | -------------------------------------------------------------------------------- /lib/parseMarkdownFile.ts: -------------------------------------------------------------------------------- 1 | import * as matter from 'gray-matter' 2 | import rehypeRaw from 'rehype-raw' 3 | import rehypeStringify from 'rehype-stringify' 4 | import remarkParse from 'remark-parse' 5 | import remarkRehype from 'remark-rehype' 6 | import { unified } from 'unified' 7 | 8 | export default async function parseMarkdownFile (path: string) { 9 | const matterResult = await matter.read(path) 10 | 11 | const convertedData = await unified() 12 | .use(remarkParse) 13 | .use(remarkRehype, { allowDangerousHtml: true }) 14 | .use(rehypeRaw) 15 | .use(rehypeStringify) 16 | .process(matterResult.content) 17 | 18 | return { 19 | html: convertedData.value, 20 | meta: matterResult.data, 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /data/infrastructure/GitHub_Actions.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'GitHub Actions' 3 | date: '2022-10-11' 4 | --- 5 | 6 | Automate, customize, and execute your software development workflows right in your repository with GitHub Actions. You can discover, create, and share actions to perform any job you'd like, including CI/CD, and combine actions in a completely customized workflow. 7 | 8 | https://docs.github.com/en/actions 9 | 10 | 11 | -------------------------------------------------------------------------------- /pages/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import type { NextPage } from 'next' 3 | import Head from 'next/head' 4 | 5 | import mainData from '../data/main.json' 6 | import { PagesLayout } from '../layouts' 7 | import { ContentEditor } from '../components' 8 | 9 | const Home: NextPage = () => { 10 | 11 | const onSaveContent = (content: object) => { 12 | console.log("content: ", content); // eslint-disable-line 13 | } 14 | 15 | return ( 16 | 17 | 18 | {mainData.title} 19 | 20 |

Personal learn path

21 |

Add source

22 | 23 |
24 | ) 25 | } 26 | 27 | export default Home 28 | 29 | -------------------------------------------------------------------------------- /components/Footer/Footer.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default function Footer () { 4 | return ( 5 | 22 | ) 23 | } 24 | -------------------------------------------------------------------------------- /components/Menu/Menu.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Link from 'next/link' 3 | import { NextRouter } from 'next/router' 4 | import style from './menu.module.css' 5 | 6 | interface WithRouterProps { 7 | router: NextRouter 8 | } 9 | 10 | interface ComposedComponent extends WithRouterProps { 11 | children: string 12 | href: string 13 | } 14 | 15 | export default function Menu () { 16 | return ( 17 | 25 | ) 26 | } 27 | -------------------------------------------------------------------------------- /e2e/example.spec.ts: -------------------------------------------------------------------------------- 1 | import { test, expect } from '@playwright/test'; 2 | 3 | test('homepage has title and links to intro page', async ({ page }) => { 4 | // await page.goto('http://localhost:3000/'); WARNING: for local 5 | await page.goto('https://easy-deep-learning.github.io/personal-learn-path/'); 6 | 7 | // Expect a title "to contain" a substring. 8 | await expect(page).toHaveTitle(/My personal/); 9 | 10 | // create a locator 11 | const getTestTasksPage = page.getByRole('link', { name: 'Тестовые задания' }); 12 | 13 | // Expect an attribute "to be strictly equal" to the value. 14 | await expect(getTestTasksPage).toHaveAttribute('href', '/test-works'); 15 | 16 | // Click the get started link. 17 | await getTestTasksPage.click(); 18 | 19 | // Expects the URL to contain intro. 20 | await expect(page).toHaveURL(/.*works/); 21 | }); 22 | -------------------------------------------------------------------------------- /data/test-works/my-first-test-work.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'Моя первая тестовая работа' 3 | date: '2009-11-30' 4 | --- 5 | 6 |
7 |
9 |

10 | Моя первая тестовая работа - верстка сайта "Салоны красоты Великого Новгорода" 11 |

12 |

13 | На данный момент проект salon-vn.ru является молодым быстроразвивающимся сайтом. 14 | Наша работа нацелена на то, чтобы помочь найти интересующий Вас салон красоты в Великом Новгороде и области, 15 | выбрать из огромного количества именно тот, который Вам подходит по ценам на услуги и расположению. 16 |

17 |
18 |
«alexbaumgertner.github.io/test-works/salonvn», 19 | Салоны красоты Великого Новгорода 20 |
21 | 22 |
23 | -------------------------------------------------------------------------------- /.github/workflows/playwright.yml: -------------------------------------------------------------------------------- 1 | name: Playwright Tests 2 | on: 3 | push: 4 | branches: [main] 5 | pull_request: 6 | branches: [main] 7 | jobs: 8 | test: 9 | timeout-minutes: 60 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v3 13 | 14 | - uses: actions/setup-node@v3 15 | with: 16 | node-version: 18 17 | 18 | - uses: pnpm/action-setup@v2.2.2 19 | with: 20 | version: 7.12.2 21 | 22 | - name: Install dependencies 23 | run: pnpm install --no-frozen-lockfile 24 | 25 | - name: Install Playwright Browsers 26 | run: npx playwright install --with-deps 27 | 28 | - name: Run Playwright tests 29 | run: npx playwright test 30 | 31 | - uses: actions/upload-artifact@v3 32 | if: always() 33 | with: 34 | name: playwright-report 35 | path: playwright-report/ 36 | retention-days: 30 37 | -------------------------------------------------------------------------------- /pages/comments/[pageid]/edit.js: -------------------------------------------------------------------------------- 1 | import { useRouter } from 'next/router' 2 | import useSWR from 'swr' 3 | import CommentForm from '../../../components/Form/CommentForm' 4 | 5 | const fetcher = (url) => 6 | fetch(url) 7 | .then((res) => res.json()) 8 | .then((json) => json.data) 9 | 10 | const EditComment = () => { 11 | const router = useRouter() 12 | const { pageid } = router.query 13 | const { data: comment, error } = useSWR(pageid ? `/api/comments/${pageid}` : null, fetcher) 14 | 15 | if (error) return

Failed to load

16 | if (!comment) return

Loading...

17 | 18 | const commentForm = { 19 | name: comment.name, 20 | email: comment.email, 21 | movie_id: comment.movie_id, 22 | text: comment.text, 23 | date: comment.date, 24 | } 25 | 26 | return 27 | } 28 | 29 | export default EditComment 30 | -------------------------------------------------------------------------------- /data/soft-skills/jedi-technics.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'Джедайские техники. Максим Дорофеев.' 3 | date: '2022-10-11' 4 | --- 5 | 6 | Недавно вышла книга Путь джедая Максима Дорофеева. Перед тем как прочитать её, я решил освежить в памяти предыдущую — Джедайские техники. Заодно законспектировал. 7 | 8 | Вообще, то, что я выработал сам для себя и как работаю последние лет 5, — напоминает джедайские техники. Просто уровень «задротскости» у Дорофеева повыше. Но, например, работа со справочниками, встречами и ежедневными задачами — повторяет его методику. 9 | 10 | Работа с проектами по Дорофееву напомнила мне GTD, но GTD я терпеть не могу, а вот «джедайство» почему-то зашло. Возможно, потому что Дорофеев описывает, как работают (а точнее мешают нам работать) когнитивные искажения. Возможно, потому что написано простыми словами. Но в любом случае, поехали. 11 | 12 | И это, да, конспект огромный, приготовьте там себе перекусить что ли. 13 | 14 | https://bespoyasov.ru/blog/jedi-technics/ 15 | -------------------------------------------------------------------------------- /pages/api/comments/index.js: -------------------------------------------------------------------------------- 1 | import dbConnect from '../../../lib/dbConnect' 2 | import Comments from '../../../models/Comments' 3 | 4 | export default async function handler(req, res) { 5 | const { method } = req 6 | 7 | await dbConnect() 8 | 9 | switch (method) { 10 | case 'GET': 11 | try { 12 | const pets = await Comments.find({}).limit(25) /* find all the data in our database */ 13 | res.status(200).json({ success: true, data: pets }) 14 | } catch (error) { 15 | res.status(400).json({ success: false }) 16 | } 17 | break 18 | case 'POST': 19 | try { 20 | const pet = await Comments.create( 21 | req.body 22 | ) /* create a new model in the database */ 23 | res.status(201).json({ success: true, data: pet }) 24 | } catch (error) { 25 | res.status(400).json({ success: false }) 26 | } 27 | break 28 | default: 29 | res.status(400).json({ success: false }) 30 | break 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /layouts/PageLayout/PagesLayout.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Head from 'next/head' 3 | import { 4 | Header, 5 | Footer, 6 | } from '../../components' 7 | import mainData from '../../data/main.json' 8 | 9 | import pageLayoutStyles from './pageLayout.module.css' 10 | 11 | type PagesLayoutProps = { 12 | children: React.ReactNode 13 | } 14 | 15 | export default function PagesLayout ({ children }: PagesLayoutProps) { 16 | return ( 17 | <> 18 |
19 | 20 | 21 | 22 | 26 | 27 | {mainData.title} 28 | 29 | 30 |
31 | 32 |
{children}
33 |
34 | 35 |