├── .gitignore
├── LICENSE
├── README.md
├── package.json
├── public
├── avatars
│ └── user_icon.jpg
├── favicon.ico
├── index.html
└── robots.txt
├── src
├── App.tsx
├── ColorModeSwitcher.tsx
├── assets
│ ├── images
│ │ ├── cover_images
│ │ │ ├── blog_1.png
│ │ │ ├── blog_2.png
│ │ │ ├── blog_3.png
│ │ │ ├── image_gallery.png
│ │ │ ├── notebook_app.png
│ │ │ └── portfolio.png
│ │ └── placeholder.png
│ └── stylesheets
│ │ └── carousel.css
├── components
│ ├── carousel.tsx
│ ├── footer-signup.tsx
│ ├── hero-section.tsx
│ ├── home-page.tsx
│ ├── lazy-image.tsx
│ ├── motion
│ │ └── motion.tsx
│ ├── note-form.tsx
│ ├── note-modal.tsx
│ ├── notes-list.tsx
│ ├── page-footer.tsx
│ ├── repositories-list-item.tsx
│ ├── repositories-list.tsx
│ └── top-nav.tsx
├── configs
│ └── site-config.ts
├── data
│ ├── cover_images.ts
│ └── repositories-list.ts
├── index.tsx
└── types.d.ts
├── static.json
├── tsconfig.json
└── yarn.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | MIT License
3 |
4 | Copyright (c) 2021 Muhammad Ahmad
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy
7 | of this software and associated documentation files (the "Software"), to deal
8 | in the Software without restriction, including without limitation the rights
9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | copies of the Software, and to permit persons to whom the Software is
11 | furnished to do so, subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be included in all
14 | copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | This is a notebook app which will help you to crate notes for your daily work.
2 |
3 | 
4 |
5 | ## Built with
6 | - [Reactjs](https://reactjs.org/)
7 | - [Typescript](https://www.typescriptlang.org/)
8 | - [Chakra UI](https://chakra-ui.com)
9 | - [react icons](https://react-icons.github.io/react-icons/)
10 |
11 | ## View and copy code of your favourite components
12 | [TemplatesKart website](https://templateskart.com/projects/notebook-app)
13 |
14 | ## License
15 |
16 | Licensed under the MIT License. Feel free to use parts of the code in your own projects with attribution!
17 |
18 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "notebook-app",
3 | "version": "0.1.0",
4 | "private": true,
5 | "homepage": "https://ma-ahmad.github.io/notebook",
6 | "dependencies": {
7 | "@chakra-ui/icons": "^1.0.6",
8 | "@chakra-ui/react": "^1.0.0",
9 | "@emotion/react": "^11.0.0",
10 | "@emotion/styled": "^11.0.0",
11 | "@testing-library/jest-dom": "^5.9.0",
12 | "@testing-library/react": "^10.2.1",
13 | "@testing-library/user-event": "^12.0.2",
14 | "@types/jest": "^25.0.0",
15 | "@types/node": "^12.0.0",
16 | "@types/react": "^16.9.0",
17 | "@types/react-dom": "^16.9.0",
18 | "@types/react-router-dom": "^5.1.7",
19 | "blurhash": "^1.1.3",
20 | "framer-motion": ">=3.0.0",
21 | "gh-pages": "^3.1.0",
22 | "nanoid": "^3.1.22",
23 | "react": "^17.0.1",
24 | "react-blurhash": "^0.1.3",
25 | "react-dom": "^17.0.1",
26 | "react-hook-form": "^6.15.5",
27 | "react-icons": "^4.2.0",
28 | "react-progressive-image": "^0.6.0",
29 | "react-router-dom": "^5.2.0",
30 | "react-scripts": "4.0.3",
31 | "react-stack-grid": "^0.7.1",
32 | "typescript": "^3.9.5",
33 | "web-vitals": "^0.2.2"
34 | },
35 | "scripts": {
36 | "start": "react-scripts start",
37 | "predeploy": "yarn run build",
38 | "deploy": "gh-pages -d build",
39 | "build": "react-scripts build",
40 | "test": "react-scripts test",
41 | "eject": "react-scripts eject"
42 | },
43 | "eslintConfig": {
44 | "extends": "react-app"
45 | },
46 | "browserslist": {
47 | "production": [
48 | ">0.2%",
49 | "not dead",
50 | "not op_mini all"
51 | ],
52 | "development": [
53 | "last 1 chrome version",
54 | "last 1 firefox version",
55 | "last 1 safari version"
56 | ]
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/public/avatars/user_icon.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MA-Ahmad/notebook/e22304c4c67f8d346aeb7cf09ddc7a893a5881f9/public/avatars/user_icon.jpg
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MA-Ahmad/notebook/e22304c4c67f8d346aeb7cf09ddc7a893a5881f9/public/favicon.ico
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | Notebook App
28 |
29 |
30 |
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/src/App.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { Switch, Route, Redirect } from "react-router-dom";
3 | import { ChakraProvider, Box, theme } from "@chakra-ui/react";
4 | import { TopNav } from "./components/top-nav";
5 | import { PageFooter } from "./components/page-footer";
6 | import RepositoriesList from "./components/repositories-list";
7 | import { HomePage } from "./components/home-page";
8 | import { RouteComponentProps, withRouter } from "react-router";
9 |
10 | type AppComponentProps = RouteComponentProps;
11 |
12 | const App: React.FC = ({ history }) => {
13 | const [notes, setNotes] = React.useState([]);
14 |
15 | React.useEffect(() => {
16 | const dummyNotes = [
17 | {
18 | id: "Odork5n5jPVd0wvm0w_dY",
19 | title: "Hey 👋",
20 | body:
21 | "I'm dummy note here. Try to update me. Click me to see my remaining part. You can also delete me 😔. But I'll be here again by reopening the app link 😃. "
22 | }
23 | ];
24 | setNotes(dummyNotes);
25 | }, []);
26 |
27 | const handleNoteCreate = (note: note) => {
28 | const newNotesState: note[] = [...notes];
29 | newNotesState.push(note);
30 | setNotes(newNotesState);
31 | if (history.location.pathname !== "/") history.push("/");
32 | };
33 |
34 | return (
35 |
36 |
37 |
38 |
39 |
40 | }
44 | />
45 |
46 |
47 |
48 |
49 |
50 | );
51 | };
52 |
53 | export default withRouter(App);
54 |
--------------------------------------------------------------------------------
/src/ColorModeSwitcher.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 | import {
3 | useColorMode,
4 | useColorModeValue,
5 | IconButton,
6 | IconButtonProps,
7 | } from "@chakra-ui/react"
8 | import { FaMoon, FaSun } from "react-icons/fa"
9 |
10 | type ColorModeSwitcherProps = Omit
11 |
12 | export const ColorModeSwitcher: React.FC = (props) => {
13 | const { toggleColorMode } = useColorMode()
14 | const text = useColorModeValue("dark", "light")
15 | const SwitchIcon = useColorModeValue(FaMoon, FaSun)
16 |
17 | return (
18 | }
26 | aria-label={`Switch to ${text} mode`}
27 | {...props}
28 | />
29 | )
30 | }
31 |
--------------------------------------------------------------------------------
/src/assets/images/cover_images/blog_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MA-Ahmad/notebook/e22304c4c67f8d346aeb7cf09ddc7a893a5881f9/src/assets/images/cover_images/blog_1.png
--------------------------------------------------------------------------------
/src/assets/images/cover_images/blog_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MA-Ahmad/notebook/e22304c4c67f8d346aeb7cf09ddc7a893a5881f9/src/assets/images/cover_images/blog_2.png
--------------------------------------------------------------------------------
/src/assets/images/cover_images/blog_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MA-Ahmad/notebook/e22304c4c67f8d346aeb7cf09ddc7a893a5881f9/src/assets/images/cover_images/blog_3.png
--------------------------------------------------------------------------------
/src/assets/images/cover_images/image_gallery.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MA-Ahmad/notebook/e22304c4c67f8d346aeb7cf09ddc7a893a5881f9/src/assets/images/cover_images/image_gallery.png
--------------------------------------------------------------------------------
/src/assets/images/cover_images/notebook_app.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MA-Ahmad/notebook/e22304c4c67f8d346aeb7cf09ddc7a893a5881f9/src/assets/images/cover_images/notebook_app.png
--------------------------------------------------------------------------------
/src/assets/images/cover_images/portfolio.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MA-Ahmad/notebook/e22304c4c67f8d346aeb7cf09ddc7a893a5881f9/src/assets/images/cover_images/portfolio.png
--------------------------------------------------------------------------------
/src/assets/images/placeholder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MA-Ahmad/notebook/e22304c4c67f8d346aeb7cf09ddc7a893a5881f9/src/assets/images/placeholder.png
--------------------------------------------------------------------------------
/src/assets/stylesheets/carousel.css:
--------------------------------------------------------------------------------
1 | .carousel-container {
2 | width: 100%;
3 | height: 100%;
4 | position: relative;
5 | display: flex;
6 | justify-content: center;
7 | align-items: center;
8 | }
9 |
10 | .next,
11 | .prev {
12 | top: calc(50% - 20px);
13 | position: absolute;
14 | background: white;
15 | color: red;
16 | border-radius: 30px;
17 | width: 40px;
18 | height: 40px;
19 | display: flex;
20 | justify-content: center;
21 | align-items: center;
22 | user-select: none;
23 | cursor: pointer;
24 | font-weight: bold;
25 | font-size: 18px;
26 | z-index: 2;
27 | }
28 |
29 | .next {
30 | right: 50px;
31 | }
32 |
33 | .prev {
34 | left: 50px;
35 | }
36 |
37 | .carousel-container img {
38 | position: absolute;
39 | max-width: 100vw;
40 | max-height: 70vh;
41 | border-radius: 5px;
42 | }
43 |
--------------------------------------------------------------------------------
/src/components/carousel.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import {
3 | ModalContent,
4 | Modal,
5 | ModalBody,
6 | ModalOverlay,
7 | IconButton
8 | } from "@chakra-ui/react";
9 | import { ChevronRightIcon, ChevronLeftIcon } from "@chakra-ui/icons";
10 | import coverImages from "data/cover_images";
11 | import { motion, AnimatePresence } from "framer-motion";
12 | import "assets/stylesheets/carousel.css";
13 |
14 | const variants = {
15 | enter: (direction: number) => {
16 | return {
17 | x: direction > 0 ? 1000 : -1000,
18 | opacity: 0
19 | };
20 | },
21 | center: {
22 | zIndex: 1,
23 | x: 0,
24 | opacity: 1
25 | },
26 | exit: (direction: number) => {
27 | return {
28 | zIndex: 0,
29 | x: direction < 0 ? 1000 : -1000,
30 | opacity: 0
31 | };
32 | }
33 | };
34 |
35 | const swipeConfidenceThreshold = 10000;
36 | const swipePower = (offset: number, velocity: number) => {
37 | return Math.abs(offset) * velocity;
38 | };
39 |
40 | export interface CarouselProps {
41 | onOpen: () => void;
42 | onClose: () => void;
43 | isOpen: boolean;
44 | repoId: number;
45 | }
46 |
47 | const Carousel: React.SFC = ({
48 | onOpen,
49 | onClose,
50 | isOpen,
51 | repoId
52 | }) => {
53 | const [[page, direction], setPage] = React.useState([0, 0]);
54 | const [imageIndex, setImageIndex] = React.useState(0);
55 |
56 | const paginate = (newDirection: number) => {
57 | setPage([page + newDirection, newDirection]);
58 | };
59 |
60 | React.useEffect(() => {
61 | setImageIndex(repoId);
62 | }, [repoId]);
63 |
64 | const nextImage = (newDirection: number) => {
65 | paginate(newDirection);
66 | setImageIndex(imageIndex + 1 < coverImages.length ? imageIndex + 1 : 0);
67 | };
68 |
69 | const prevImage = (newDirection: number) => {
70 | paginate(newDirection);
71 | setImageIndex(
72 | 0 === imageIndex ? coverImages.length - 1 : imageIndex - 1
73 | );
74 | };
75 |
76 | return (
77 |
78 |
79 |
80 |
81 |
82 |
83 | {
99 | const swipe = swipePower(offset.x, velocity.x);
100 |
101 | if (swipe < -swipeConfidenceThreshold) {
102 | paginate(1);
103 | } else if (swipe > swipeConfidenceThreshold) {
104 | paginate(-1);
105 | }
106 | }}
107 | />
108 |
109 |
nextImage(1)}>
110 | }
113 | cursor="pointer"
114 | as={ChevronRightIcon}
115 | size="md"
116 | colorScheme="teal"
117 | borderRadius="full"
118 | />
119 |
120 |
121 |
prevImage(-1)}>
122 | }
125 | cursor="pointer"
126 | as={ChevronLeftIcon}
127 | size="md"
128 | colorScheme="teal"
129 | borderRadius="full"
130 | />
131 |
132 |
133 |
134 |
135 |
136 | );
137 | };
138 |
139 | export default Carousel;
140 |
--------------------------------------------------------------------------------
/src/components/footer-signup.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { Box, Button, Heading, Input, Text } from "@chakra-ui/react";
3 |
4 | export function FooterSignup() {
5 | return (
6 | <>
7 |
8 | Be the first to know
9 |
10 |
11 | Get notified about the upcoming sessions, news, articles, jobs, and
12 | opensource projects.
13 |
14 |
15 |
47 | >
48 | );
49 | }
50 |
--------------------------------------------------------------------------------
/src/components/hero-section.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { Heading, Text, Flex, SlideFade } from "@chakra-ui/react";
3 | import { motion } from "framer-motion";
4 |
5 | export default function HeroSection() {
6 | return (
7 | <>
8 |
9 |
10 |
11 |
16 | Make notes for
17 |
23 | your daily work
24 |
25 |
26 |
27 |
28 |
29 | >
30 | );
31 | }
32 |
--------------------------------------------------------------------------------
/src/components/home-page.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { useDisclosure, useToast } from "@chakra-ui/react";
3 | import NotesList from "./notes-list";
4 | import HeroSection from "./hero-section";
5 | import NoteForm from "./note-form";
6 | import { AnimatePage } from "./motion/motion";
7 |
8 | export interface HomePageProps {
9 | notes: note[];
10 | setNotes: (note: note[]) => void;
11 | }
12 |
13 | export const HomePage: React.SFC = ({ notes, setNotes }) => {
14 | const { isOpen, onOpen, onClose } = useDisclosure();
15 | const [selectedNote, setSelectedNote] = React.useState();
16 | const toast = useToast();
17 |
18 | const handleClick = (id: string) => {
19 | const selectedNote = notes.find((note: note) => note.id === id);
20 | setSelectedNote(selectedNote);
21 | onOpen();
22 | };
23 |
24 | const handleNoteUpdate = (newNote: note) => {
25 | const newNotesState: note[] = [...notes];
26 | const index = notes.findIndex((note: note) => note.id === newNote.id);
27 | newNotesState[index] = newNote;
28 | setNotes(newNotesState);
29 | showToast();
30 | };
31 |
32 | const showToast = () => {
33 | toast({
34 | title: "Note updated.",
35 | status: "success",
36 | position: "top",
37 | duration: 2000,
38 | isClosable: true
39 | });
40 | };
41 |
42 | return (
43 | <>
44 |
45 | {notes.length ? (
46 |
51 | ) : (
52 |
53 | )}
54 | {isOpen ? (
55 |
61 | ) : (
62 | ""
63 | )}
64 |
65 | >
66 | );
67 | };
68 |
--------------------------------------------------------------------------------
/src/components/lazy-image.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import ProgressiveImage from "react-progressive-image";
3 | import { BlurhashCanvas } from "react-blurhash";
4 | import { Image } from "@chakra-ui/react";
5 | import placeholder from 'assets/images/placeholder.png';
6 |
7 | type LazyImageProps = {
8 | id: number;
9 | src: string;
10 | handleClick: (id: number) => void;
11 | blurHash: string;
12 | };
13 |
14 | const LazyImage = (props: LazyImageProps) => {
15 | const { src, handleClick, blurHash, id } = props;
16 |
17 | return (
18 |
24 | {(src, loading) => {
25 | return loading ? (
26 |
32 | ) : (
33 | handleClick(id)}
43 | />
44 | );
45 | }}
46 |
47 | );
48 | };
49 |
50 | export default LazyImage;
51 |
--------------------------------------------------------------------------------
/src/components/motion/motion.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { Box, BoxProps } from "@chakra-ui/react";
3 | import { AnimatePresence, motion } from "framer-motion";
4 |
5 | const MotionBox = motion.custom(Box);
6 |
7 | export const AnimatePage = ({ children }) => {
8 | return (
9 |
10 |
22 | {children}
23 |
24 |
25 | );
26 | };
27 |
--------------------------------------------------------------------------------
/src/components/note-form.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import {
3 | Button,
4 | ModalContent,
5 | Modal,
6 | ModalBody,
7 | ModalOverlay,
8 | ModalFooter,
9 | ModalHeader,
10 | ModalCloseButton,
11 | FormControl,
12 | FormLabel,
13 | FormErrorMessage,
14 | Input,
15 | Textarea
16 | } from "@chakra-ui/react";
17 | import { nanoid } from "nanoid";
18 | import { SubmitHandler } from "react-hook-form";
19 | import { useForm } from "react-hook-form";
20 |
21 | export interface NoteFormProps {
22 | isOpen: boolean;
23 | onClose: () => void;
24 | selectedNote?: note;
25 | handleNoteCreate?: (note: note) => void;
26 | handleNoteUpdate?: (note: note) => void;
27 | }
28 |
29 | type FormInputs = {
30 | title: string;
31 | body: string;
32 | };
33 |
34 | const NoteForm: React.SFC = ({
35 | isOpen,
36 | onClose,
37 | selectedNote,
38 | handleNoteCreate,
39 | handleNoteUpdate
40 | }) => {
41 | const { register, handleSubmit, formState, errors } = useForm({
42 | mode: "onChange"
43 | });
44 |
45 | const onSubmit: SubmitHandler = data => {
46 | let newNote: note = {
47 | id: "",
48 | title: data.title,
49 | body: data.body
50 | };
51 | if (handleNoteCreate) {
52 | newNote.id = nanoid();
53 | if (handleNoteCreate) handleNoteCreate(newNote);
54 | } else {
55 | newNote.id = selectedNote ? selectedNote.id : "";
56 | if (handleNoteUpdate) handleNoteUpdate(newNote);
57 | }
58 | onClose();
59 | };
60 |
61 | const validateTitle = (value: string) => {
62 | if (!value) {
63 | return "Title is required";
64 | } else return true;
65 | };
66 |
67 | const validateBody = (value: string) => {
68 | if (!value) {
69 | return "Body is required";
70 | } else return true;
71 | };
72 |
73 | return (
74 |
81 |
82 |
83 |
126 |
127 |
128 | );
129 | };
130 |
131 | export default NoteForm;
132 |
--------------------------------------------------------------------------------
/src/components/note-modal.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import {
3 | ModalContent,
4 | Modal,
5 | ModalBody,
6 | ModalOverlay,
7 | ModalHeader,
8 | ModalCloseButton
9 | } from "@chakra-ui/react";
10 | import { motion, AnimatePresence } from "framer-motion";
11 |
12 | export interface NoteFormProps {
13 | isOpen: boolean;
14 | onClose: () => void;
15 | selectedNote?: note;
16 | }
17 |
18 | const NoteModal: React.SFC = ({
19 | isOpen,
20 | onClose,
21 | selectedNote
22 | }) => {
23 | return (
24 |
25 |
26 |
33 |
34 |
35 |
36 |
37 | {selectedNote?.title}
38 |
39 |
40 |
41 |
42 | {selectedNote?.body}
43 |
44 |
45 |
46 |
47 |
48 | );
49 | };
50 |
51 | export default NoteModal;
52 |
--------------------------------------------------------------------------------
/src/components/notes-list.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import {
3 | Box,
4 | Stack,
5 | Heading,
6 | Text,
7 | Flex,
8 | Center,
9 | Fade,
10 | Icon,
11 | useToast,
12 | useColorModeValue,
13 | HStack,
14 | useDisclosure
15 | } from "@chakra-ui/react";
16 | import { DeleteIcon, EditIcon } from "@chakra-ui/icons";
17 | import { AnimateSharedLayout, motion } from "framer-motion";
18 | import NoteModal from "./note-modal";
19 | import StackGrid from "react-stack-grid";
20 |
21 | export interface NotesListProps {
22 | notes: note[];
23 | handleClick: (id: string) => void;
24 | setNotes: (note: note[]) => void;
25 | }
26 |
27 | const NotesList: React.SFC = ({
28 | notes,
29 | handleClick,
30 | setNotes
31 | }) => {
32 | const bg = useColorModeValue("white", "#2f3244");
33 | const [selectedNote, setSelectedNote] = React.useState();
34 | const toast = useToast();
35 | const { isOpen, onOpen, onClose } = useDisclosure();
36 |
37 | const onDelete = (
38 | id: string,
39 | e: React.MouseEvent
40 | ) => {
41 | const newNotes: note[] = notes.filter((note: note) => note.id !== id);
42 | setNotes(newNotes);
43 | showToast();
44 | e.stopPropagation();
45 | };
46 |
47 | const onClick = (id: string, e: React.MouseEvent) => {
48 | handleClick(id);
49 | e.stopPropagation();
50 | };
51 |
52 | const handleSelectedNote = (note: note) => {
53 | setSelectedNote(note);
54 | onOpen();
55 | };
56 |
57 | const showToast = () => {
58 | toast({
59 | title: "Note deleted.",
60 | status: "success",
61 | position: "top",
62 | duration: 2000,
63 | isClosable: true
64 | });
65 | };
66 |
67 | return (
68 | <>
69 |
70 |
71 | {/* */}
78 |
79 | {notes.map(note => (
80 |
81 | handleSelectedNote(note)}
85 | >
86 |
87 | handleClick(note.id, true)}
99 | >
100 |
101 |
106 |
107 |
114 | Note
115 |
116 |
117 |
121 |
122 | onClick(note.id, e)}
130 | />
131 | onDelete(note.id, e)}
139 | />
140 |
141 |
142 |
143 |
149 | {note.title}
150 |
151 |
152 |
157 | {note.body}
158 |
159 |
160 |
161 |
162 |
163 |
164 | ))}
165 |
166 | {/* */}
167 |
168 | {isOpen ? (
169 |
174 | ) : (
175 | ""
176 | )}
177 |
178 | >
179 | );
180 | };
181 |
182 | export default NotesList;
183 |
--------------------------------------------------------------------------------
/src/components/page-footer.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import {
3 | Box,
4 | Link as ChakraLink,
5 | SimpleGrid,
6 | Stack,
7 | Text,
8 | Popover,
9 | PopoverTrigger,
10 | Portal,
11 | PopoverContent,
12 | PopoverArrow,
13 | PopoverCloseButton,
14 | PopoverBody,
15 | IconButton
16 | } from "@chakra-ui/react";
17 | import siteConfig from "configs/site-config";
18 | import { FooterSignup } from "./footer-signup";
19 | import { Link } from "react-router-dom";
20 | import {
21 | FaGithub,
22 | FaDev,
23 | FaLinkedin,
24 | FaQuora,
25 | FaTwitter
26 | } from "react-icons/fa";
27 |
28 | type ExternalFooterLinkProps = {
29 | href: string;
30 | text: string;
31 | isExternal?: boolean;
32 | };
33 |
34 | const ExternalFooterLink = (props: ExternalFooterLinkProps) => {
35 | const { href, text, isExternal = true } = props;
36 |
37 | return (
38 |
46 | {text}
47 |
48 | );
49 | };
50 |
51 | type InternalFooterLinkProps = {
52 | href: string;
53 | text: string;
54 | };
55 |
56 | const InternalFooterLink = (props: InternalFooterLinkProps) => {
57 | const { href, text } = props;
58 |
59 | return (
60 |
61 |
62 |
69 | {text}
70 |
71 |
72 |
73 | );
74 | };
75 |
76 | const iconProps = {
77 | variant: "ghost",
78 | size: "lg",
79 | isRound: true
80 | };
81 |
82 | type ExternalSocialLinkProps = {
83 | href: string;
84 | label: string;
85 | isExternal?: boolean;
86 | type: string;
87 | icon: any;
88 | };
89 |
90 | const ExternalSocialLink = (props: ExternalSocialLinkProps) => {
91 | const { href, label, icon, type, isExternal = true } = props;
92 |
93 | return (
94 |
103 | );
104 | };
105 |
106 | export function PageFooter() {
107 | return (
108 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
127 |
128 |
129 |
133 |
134 |
135 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
153 | Social Accounts
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
168 | }
171 | type="gray"
172 | label="Github Account"
173 | />
174 | }
177 | type="gray"
178 | label="Dev Account"
179 | />
180 | }
183 | type="linkedin"
184 | label="LinkedIn Account"
185 | />
186 | }
189 | type="twitter"
190 | label="Twitter Account"
191 | />
192 | }
195 | type="red"
196 | label="Quora Account"
197 | />
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
210 |
211 |
212 |
217 |
218 |
219 |
220 |
221 | Made with 🧡 by{" "}
222 |
234 | Muhammad Ahmad
235 | {" "}
236 |
237 |
238 |
239 |
240 |
241 |
242 | );
243 | }
244 |
--------------------------------------------------------------------------------
/src/components/repositories-list-item.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import {
3 | Box,
4 | Badge,
5 | Flex,
6 | Center,
7 | Link,
8 | HStack,
9 | Tooltip,
10 | useColorModeValue,
11 | useDisclosure
12 | } from "@chakra-ui/react";
13 | import { StarIcon, ExternalLinkIcon } from "@chakra-ui/icons";
14 | import Carousel from "./carousel";
15 | import LazyImage from "./lazy-image";
16 |
17 | interface RepositoriesListItemProps {
18 | repo: repo;
19 | }
20 |
21 | const RepositoriesListItem: React.SFC = ({
22 | repo
23 | }) => {
24 | const [repoId, setRepoId] = React.useState(0);
25 | const bg = useColorModeValue("white", "#2f3244");
26 | const textColor = useColorModeValue("gray.600", "#b5b1b1");
27 | const { isOpen, onOpen, onClose } = useDisclosure();
28 |
29 | const handleClick = (id: number) => {
30 | setRepoId(id);
31 | onOpen();
32 | };
33 |
34 | return (
35 | <>
36 |
37 |
49 | {/* handleClick(repo.coverImage)}
59 | /> */}
60 |
66 |
67 |
68 |
69 |
78 |
83 | {repo.title}
84 |
85 |
86 |
87 |
88 | {repo.stars ? (
89 |
94 |
95 |
96 |
97 |
98 | {repo.stars}
99 |
100 |
101 |
102 |
103 | ) : (
104 | ""
105 | )}
106 |
107 |
108 |
109 |
110 | {repo.technologies.map((tech, index) => (
111 |
118 | {tech.name}
119 |
120 | ))}
121 |
122 |
123 |
124 | {repo.desc}
125 |
126 |
127 |
128 |
129 |
135 | >
136 | );
137 | };
138 |
139 | export default RepositoriesListItem;
140 |
--------------------------------------------------------------------------------
/src/components/repositories-list.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import {
3 | Heading,
4 | Box,
5 | SimpleGrid,
6 | Flex,
7 | } from "@chakra-ui/react";
8 | import repositoriesList from "data/repositories-list";
9 | import { motion } from "framer-motion";
10 | import RepositoriesListItem from "./repositories-list-item";
11 | import { AnimatePage } from "./motion/motion";
12 |
13 | const RepositoriesList = () => {
14 | return (
15 | <>
16 |
17 |
18 |
19 |
30 | Repositories
31 |
32 |
33 | {/* */}
34 |
41 | {repositoriesList.map((repo, index) => (
42 |
43 |
44 |
45 | ))}
46 |
47 | {/* */}
48 |
49 |
50 | >
51 | );
52 | };
53 |
54 | export default RepositoriesList;
55 |
--------------------------------------------------------------------------------
/src/components/top-nav.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import {
3 | Box,
4 | Text,
5 | Flex,
6 | Spacer,
7 | Heading,
8 | Menu,
9 | MenuItem,
10 | MenuDivider,
11 | MenuButton,
12 | IconButton,
13 | MenuList,
14 | HStack,
15 | Button,
16 | useDisclosure
17 | } from "@chakra-ui/react";
18 | import { Link } from "react-router-dom";
19 | import { AddIcon, HamburgerIcon, ArrowRightIcon } from "@chakra-ui/icons";
20 | import { ColorModeSwitcher } from "ColorModeSwitcher";
21 | import NoteForm from "./note-form";
22 | import { motion } from "framer-motion";
23 |
24 | export interface TopNavProps {
25 | handleNoteCreate?: (note: note) => void;
26 | }
27 |
28 | export const TopNav: React.SFC = ({ handleNoteCreate }) => {
29 | const { isOpen, onOpen, onClose } = useDisclosure();
30 |
31 | return (
32 | <>
33 |
34 |
35 |
36 |
37 |
48 | Notebook App
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 | }
59 | bgGradient="linear(to-l, #7928CA,#FF0080)"
60 | _hover={{ bgGradient: "linear(to-r, red.500, yellow.500)" }}
61 | variant="solid"
62 | size="sm"
63 | onClick={onOpen}
64 | >
65 | Add new note
66 |
67 | }
69 | bgGradient="linear(to-l, #7928CA,#FF0080)"
70 | _hover={{ bgGradient: "linear(to-r, red.500, yellow.500)" }}
71 | variant="solid"
72 | size="sm"
73 | as={Link}
74 | to="/projects"
75 | >
76 | Open source
77 |
78 |
79 |
80 |
105 |
106 |
107 |
108 |
109 |
110 |
115 | >
116 | );
117 | };
118 |
--------------------------------------------------------------------------------
/src/configs/site-config.ts:
--------------------------------------------------------------------------------
1 | const baseUrl = 'https://github.com/MA-Ahmad/notebook';
2 |
3 | const siteConfig = {
4 | copyright: `Copyright © ${ new Date().getFullYear() } Muhammad Ahmad. All Rights Reserved.`,
5 | author: {
6 | name: 'Muhammad Ahmad',
7 | github: 'https://github.com/MA-Ahmad',
8 | twitter: 'https://twitter.com/muhammad_ahmaad',
9 | linkedin: 'https://linkedin.com/in/muhammad-ahmad20',
10 | quora: 'https://www.quora.com/profile/Muhammad-Ahmad-66',
11 | dev: 'https://dev.to/m_ahmad',
12 | email: 'muhammad.ahmad8043@gmail.com'
13 | },
14 | repo: {
15 | url: baseUrl,
16 | issuesUrl: `${ baseUrl }/issues/new`,
17 | }
18 | };
19 |
20 | export default siteConfig;
21 |
--------------------------------------------------------------------------------
/src/data/cover_images.ts:
--------------------------------------------------------------------------------
1 | import img1 from "assets/images/cover_images/blog_1.png";
2 | import img2 from "assets/images/cover_images/blog_2.png";
3 | import img3 from "assets/images/cover_images/portfolio.png";
4 | import img4 from "assets/images/cover_images/image_gallery.png";
5 | import img5 from "assets/images/cover_images/notebook_app.png";
6 | import img6 from "assets/images/cover_images/blog_3.png";
7 |
8 | const coverImages = [
9 | img1,
10 | img2,
11 | img3,
12 | img4,
13 | img5,
14 | img6
15 | ];
16 |
17 | export default coverImages;
18 |
--------------------------------------------------------------------------------
/src/data/repositories-list.ts:
--------------------------------------------------------------------------------
1 | import coverImages from "./cover_images";
2 |
3 | const repositoriesList = [
4 | {
5 | id: 0,
6 | title: "Portfolio",
7 | desc: "Personal portfolio app to show my skills and experience.",
8 | technologies: [
9 | { name: "React", color: "green" },
10 | { name: "ChakraUi", color: "teal" }
11 | ],
12 | stars: 12,
13 | githubLink: "https://github.com/MA-Ahmad/portfolio",
14 | liveLink: "https://mahmad.me/",
15 | coverImage: coverImages[2],
16 | blurHash: "U4S~x501-;~pRiNGIURjRjIoM{xbNFR*Rjay"
17 | },
18 | {
19 | id: 1,
20 | title: "Notebook App",
21 | desc: "Create notes for your daily important work.",
22 | technologies: [
23 | { name: "React", color: "green" },
24 | { name: "Typescript", color: "blue" },
25 | { name: "ChakraUi", color: "teal" }
26 | ],
27 | stars: 11,
28 | githubLink: "https://github.com/MA-Ahmad/notebook",
29 | liveLink: "",
30 | coverImage: coverImages[4],
31 | blurHash: "U4S~x6WE~WwJ=|VsMybb%NVt8_tP%2RQRknl"
32 | },
33 | {
34 | id: 2,
35 | title: "Blog App",
36 | desc: "A Blog App built by using React, ChakraUI and Formik.",
37 | technologies: [
38 | { name: "React", color: "green" },
39 | { name: "ChakraUi", color: "teal" }
40 | ],
41 | stars: 9,
42 | githubLink: "https://github.com/MA-Ahmad/reactBlog",
43 | liveLink: "https://ma-ahmad.github.io/reactBlog",
44 | coverImage: coverImages[0],
45 | blurHash: "ULM*T}IV~pxt00%LRjNG9~IVadt6?vxtD%Rj"
46 | },
47 | {
48 | id: 3,
49 | title: "Blog App + Authentication",
50 | desc: "A Blog App built by using React, Rails, ChakraUI and Formik.",
51 | technologies: [
52 | { name: "React", color: "green" },
53 | { name: "Rails", color: "red" },
54 | { name: "ChakraUi", color: "teal" }
55 | ],
56 | stars: 6,
57 | githubLink: "https://github.com/MA-Ahmad/blog-app-react-frontend",
58 | liveLink: "https://blog-frontend-react.herokuapp.com/",
59 | coverImage: coverImages[1],
60 | blurHash: "UQNTzZHr~Wtl00={M{NG0dIokDxaloO?IUnO"
61 | },
62 | {
63 | id: 4,
64 | title: "Image Gallery",
65 | desc: "This app built by using Tailwind CSS with React and Pixabay API.",
66 | technologies: [
67 | { name: "React", color: "green" },
68 | { name: "Tailwindcss", color: "telegram" }
69 | ],
70 | stars: 2,
71 | githubLink: "https://github.com/MA-Ahmad/react-image-gallery",
72 | liveLink: "",
73 | coverImage: coverImages[3],
74 | blurHash: "U]OzA2n%W;ayRPn%fkWVx]bHjFj[_NWXofay"
75 | },
76 | {
77 | id: 5,
78 | title: "Crud Demo App",
79 | desc: "A simple react+rails(RR) CRUD app with tailwindcss.",
80 | technologies: [
81 | { name: "React", color: "green" },
82 | { name: "Rails", color: "red" },
83 | { name: "Tailwindcss", color: "telegram" }
84 | ],
85 | stars: 2,
86 | githubLink: "https://github.com/MA-Ahmad/react_rails_blog",
87 | liveLink: "https://react-on-rails-blog.herokuapp.com/",
88 | coverImage: coverImages[5],
89 | blurHash: "UEPGv.9FRkfR00%N%NofItxas-j@?dD%D%fj"
90 | },
91 | {
92 | id: 6,
93 | title: "Scrapper App",
94 | desc: "A simple rails scrapper app to count html tags of a web page.",
95 | technologies: [{ name: "Rails", color: "red" }],
96 | stars: 1,
97 | githubLink: "https://github.com/MA-Ahmad/rails-app",
98 | liveLink: "https://urltohtmlapp.herokuapp.com/",
99 | coverImage: "",
100 | blurHash: "UEPGv.9FRkfR00%N%NofItxas-j@?dD%D%fj"
101 | }
102 | ];
103 |
104 | export default repositoriesList;
105 |
--------------------------------------------------------------------------------
/src/index.tsx:
--------------------------------------------------------------------------------
1 | import { Box, ColorModeScript } from "@chakra-ui/react";
2 | import { HashRouter as Router } from "react-router-dom";
3 | import * as React from "react";
4 | import ReactDOM from "react-dom";
5 | import App from "./App";
6 |
7 | ReactDOM.render(
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | ,
16 | document.getElementById("root")
17 | );
18 |
--------------------------------------------------------------------------------
/src/types.d.ts:
--------------------------------------------------------------------------------
1 | type note = {
2 | id: string,
3 | title: string;
4 | body: string;
5 | };
6 |
7 | type repo = {
8 | id: number;
9 | title: string;
10 | desc: string;
11 | technologies: {
12 | name: string;
13 | color: string;
14 | }[];
15 | stars: number;
16 | githubLink: string;
17 | liveLink: string;
18 | coverImage: string;
19 | blurHash: string;
20 | }
--------------------------------------------------------------------------------
/static.json:
--------------------------------------------------------------------------------
1 | {
2 | "root": "build/",
3 | "clean_urls": false,
4 | "routes": {
5 | "/**": "index.html"
6 | }
7 | }
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": "src",
4 | "target": "es5",
5 | "lib": [
6 | "dom",
7 | "dom.iterable",
8 | "esnext"
9 | ],
10 | "allowJs": true,
11 | "skipLibCheck": true,
12 | "esModuleInterop": true,
13 | "allowSyntheticDefaultImports": true,
14 | "strict": false,
15 | "forceConsistentCasingInFileNames": true,
16 | "noFallthroughCasesInSwitch": true,
17 | "module": "esnext",
18 | "moduleResolution": "node",
19 | "resolveJsonModule": true,
20 | "isolatedModules": true,
21 | "noEmit": true,
22 | "jsx": "react"
23 | },
24 | "include": [
25 | "src"
26 | ]
27 | }
28 |
--------------------------------------------------------------------------------