diff --git a/public/images/check.svg b/public/images/check.svg new file mode 100644 index 0000000..3f42f05 --- /dev/null +++ b/public/images/check.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/app/store.ts b/src/app/store.ts index 187d49b..4da0af8 100644 --- a/src/app/store.ts +++ b/src/app/store.ts @@ -1,6 +1,6 @@ import { configureStore, ThunkAction, Action } from '@reduxjs/toolkit' -import counterReducer from '../features/counter/counterSlice' +let counterReducer = null export function makeStore() { return configureStore({ diff --git a/src/app/types.ts b/src/app/types.ts new file mode 100644 index 0000000..f0ecb57 --- /dev/null +++ b/src/app/types.ts @@ -0,0 +1,15 @@ + +export enum EventTypes{ + AIM, + COMMENT, + DREAM +} + +export interface EventIE{ + date: Date + name: string + description: string + isModerated?: boolean + type: EventTypes +} + diff --git a/src/components/Button/btn.module.css b/src/components/Button/btn.module.css new file mode 100644 index 0000000..c8ac172 --- /dev/null +++ b/src/components/Button/btn.module.css @@ -0,0 +1,38 @@ +.btn{ + display: inline-block; + padding: 0px 24px; + height: 30px; + line-height: 30px; + font-size: 14px; + font-weight: 300; + background: #1A1F24; + color:aliceblue; + outline: none; + border:none; + cursor: pointer; + border-radius: 100px; + letter-spacing: 0.1em; + white-space: nowrap; + box-shadow: -3px -3px 8px rgb(255 255 255 / 5%), 3px 3px 8px rgb(0 0 0 / 5%); +} + +.bigBtn{ + width: 316px; + height: 52px; + line-height: 52px; + background: #1A1B1F; + box-shadow: -4px -4px 4px #1e1f24, 4px 4px 8px #16171a; + border-radius: 14px; + font-size: 13px; + letter-spacing: 0.03em; + text-transform: uppercase; + color: #FFFFFF; + text-align: center; + font-weight: 500; + border: none; + cursor: pointer; +} + +.btn:hover, .bigBtn:hover{ + background-color: #E6B98B; +} \ No newline at end of file diff --git a/src/components/Button/index.tsx b/src/components/Button/index.tsx new file mode 100644 index 0000000..053a87b --- /dev/null +++ b/src/components/Button/index.tsx @@ -0,0 +1,20 @@ +import React from "react"; +import styles from "./btn.module.css" + + +interface ButtonIE{ + class?:string + isBig?:boolean + onClick: ()=>void + children: JSX.Element|string +} + + +export const Button: React.FC = (props) =>{ + return( + + ) +} \ No newline at end of file diff --git a/src/components/Checkbox/checkbox.module.css b/src/components/Checkbox/checkbox.module.css new file mode 100644 index 0000000..bc087bf --- /dev/null +++ b/src/components/Checkbox/checkbox.module.css @@ -0,0 +1,22 @@ +.checkBox{ + display:inline-flex; + flex-direction: row; + gap:10px; + justify-content: center; + cursor: pointer; + user-select: none; + color:aliceblue; + font-size: 14px; + align-items: center; +} +.box{ + width: 22px !important; + height: 22px; + background-color: #181C22; + border-radius: 3px; + border: 0px; + display: flex; + align-items: center; + justify-content: center; + +} \ No newline at end of file diff --git a/src/components/Checkbox/index.tsx b/src/components/Checkbox/index.tsx new file mode 100644 index 0000000..1547130 --- /dev/null +++ b/src/components/Checkbox/index.tsx @@ -0,0 +1,20 @@ +import React from "react"; +import styles from "./checkbox.module.css" + +interface checkboxIE{ + active:boolean + children:JSX.Element|string + class?:string + onClick:()=>void +} + +export const Checkbox:React.FC = (props) =>{ + return( +
props.onClick()}> +
{props.active? : null}
+
+ {props.children} +
+
+ ) +} \ No newline at end of file diff --git a/src/components/EventComponent/event.module.css b/src/components/EventComponent/event.module.css new file mode 100644 index 0000000..7a35d91 --- /dev/null +++ b/src/components/EventComponent/event.module.css @@ -0,0 +1,49 @@ +.eventCard{ + width: 316px; + background: #1A1B1F; + border-radius: 14px; + font-size: 13px; + letter-spacing: 0.03em; + color: #dfdfdf; + font-weight: 500; + border: none; + cursor: pointer; + display: flex; + flex-direction: row; + align-items: center; + justify-content:space-between; + padding:10px 25px +} + +.eventCard:hover{ + box-shadow: -4px -4px 4px #1e1f24, 4px 4px 8px #16171a; +} + +.eventTypeBar{ + border-radius: 100px; + height: 10px; + width: 50px; +} + +.wrapText{ + display: flex; + flex-direction: column; + justify-content: space-between; + gap:5px +} +.date{ + display: flex; + align-items: center; + justify-content: center; + font-size: 28px; +} + +.moth{ + color: #b1b1b1; + +} +.eventWrapper{ + display: flex; + flex-direction: column; + gap:15px +} \ No newline at end of file diff --git a/src/components/EventComponent/eventCard.tsx b/src/components/EventComponent/eventCard.tsx new file mode 100644 index 0000000..48c0497 --- /dev/null +++ b/src/components/EventComponent/eventCard.tsx @@ -0,0 +1,51 @@ +import React, { useState } from "react"; +import styles from "./event.module.css" +import {EventIE, EventTypes} from "../../app/types" +import { ClientRequestArgs } from "http"; + + +const month = [ + "Январь", + "Февраль", + "Март", + "Апрель", + "Май", + "Июнь", + "Июль", + "Август", + "Сентябрь", + "Октябрь", + "Ноябрь", + "Декабрь" +] + +export const EventCard:React.FC<{args:EventIE}> = (props) =>{ + const [opened, setOpened] = useState(false) + let typeColor = props.args.type == EventTypes.AIM? "#00FF47": props.args.type == EventTypes.COMMENT? "#FF2828":"#0085FF" + + return( +
+ { + + !opened? + +
setOpened(true)}> +
+
{props.args.name}
+
+
+
+
{props.args.date.getDate()}
+
{month[props.args.date.getMonth()-1]}
+
+
+ : +
+
+ setOpened(false)}> +
+
+ } +
+ ) +} \ No newline at end of file diff --git a/src/components/EventComponent/index.tsx b/src/components/EventComponent/index.tsx new file mode 100644 index 0000000..67c8ba3 --- /dev/null +++ b/src/components/EventComponent/index.tsx @@ -0,0 +1,23 @@ +import React from "react"; +import styles from "./event.module.css" +import {EventIE, EventTypes} from "../../app/types" +import { EventCard } from "./eventCard"; + + +interface EventComponentIE{ + events: EventIE[] +} + + + +export const EventComponent:React.FC = (props) =>{ + return( +
+ { + props.events.map((event)=> + + ) + } +
+ ) +} \ No newline at end of file diff --git a/src/components/Input/index.tsx b/src/components/Input/index.tsx new file mode 100644 index 0000000..e74a000 --- /dev/null +++ b/src/components/Input/index.tsx @@ -0,0 +1,20 @@ +import React from "react"; +import styles from "./inp.module.css" + +interface InputIE{ + placeholder:string + type?: string + onChange:(value:string)=>void + class?:string +} + +export const Input:React.FC = (props) =>{ + return( + props.onChange(e.target.value)} + className={styles.inp + " " + props.class} + > + ) +} \ No newline at end of file diff --git a/src/components/Input/inp.module.css b/src/components/Input/inp.module.css new file mode 100644 index 0000000..762ae0e --- /dev/null +++ b/src/components/Input/inp.module.css @@ -0,0 +1,18 @@ +.inp{ + background: #23252C; + border-radius: 14px; + height: 40px; + line-height: 40px; + border: 0px; + padding: 0 14px; + color: #4D505B; + font-size: 12px; + width: 100%; + max-width: 400px; +} +.inp:hover{ + box-shadow: -3px -3px 8px rgb(255 255 255 / 5%), 3px 3px 8px rgb(0 0 0 / 5%); +} +.inp:focus{ + color: #fff; +} \ No newline at end of file diff --git a/src/features/counter/Counter.module.css b/src/features/counter/Counter.module.css deleted file mode 100644 index 025bb72..0000000 --- a/src/features/counter/Counter.module.css +++ /dev/null @@ -1,79 +0,0 @@ -.row { - display: flex; - align-items: center; - justify-content: center; -} - -.row > button { - margin-left: 4px; - margin-right: 8px; -} - -.row:not(:last-child) { - margin-bottom: 16px; -} - -.value { - font-size: 78px; - padding-left: 16px; - padding-right: 16px; - margin-top: 2px; - font-family: 'Courier New', Courier, monospace; -} - -.button { - appearance: none; - background: none; - font-size: 32px; - padding-left: 12px; - padding-right: 12px; - outline: none; - border: 2px solid transparent; - color: rgb(112, 76, 182); - padding-bottom: 4px; - cursor: pointer; - background-color: rgba(112, 76, 182, 0.1); - border-radius: 2px; - transition: all 0.15s; -} - -.textbox { - font-size: 32px; - padding: 2px; - width: 64px; - text-align: center; - margin-right: 4px; -} - -.button:hover, -.button:focus { - border: 2px solid rgba(112, 76, 182, 0.4); -} - -.button:active { - background-color: rgba(112, 76, 182, 0.2); -} - -.asyncButton { - composes: button; - position: relative; -} - -.asyncButton:after { - content: ''; - background-color: rgba(112, 76, 182, 0.15); - display: block; - position: absolute; - width: 100%; - height: 100%; - left: 0; - top: 0; - opacity: 0; - transition: width 1s linear, opacity 0.5s ease 1s; -} - -.asyncButton:active:after { - width: 0%; - opacity: 1; - transition: 0s; -} diff --git a/src/features/counter/Counter.spec.tsx b/src/features/counter/Counter.spec.tsx deleted file mode 100644 index 91d6afb..0000000 --- a/src/features/counter/Counter.spec.tsx +++ /dev/null @@ -1,105 +0,0 @@ -import { render, screen } from '@testing-library/react' -import user from '@testing-library/user-event' -import { Provider } from 'react-redux' - -jest.mock('./counterAPI', () => ({ - fetchCount: (amount: number) => - new Promise<{ data: number }>((resolve) => - setTimeout(() => resolve({ data: amount }), 500) - ), -})) - -import { makeStore } from '../../app/store' -import Counter from './Counter' - -describe('', () => { - it('renders the component', () => { - const store = makeStore() - - render( - - - - ) - - expect(screen.getByText('0')).toBeInTheDocument() - }) - - it('decrements the value', () => { - const store = makeStore() - - render( - - - - ) - - user.click(screen.getByRole('button', { name: /decrement value/i })) - - expect(screen.getByText('-1')).toBeInTheDocument() - }) - - it('increments the value', () => { - const store = makeStore() - - render( - - - - ) - - user.click(screen.getByRole('button', { name: /increment value/i })) - - expect(screen.getByText('1')).toBeInTheDocument() - }) - - it('increments by amount', () => { - const store = makeStore() - - render( - - - - ) - - user.type(screen.getByLabelText(/set increment amount/i), '{backspace}5') - user.click(screen.getByRole('button', { name: /add amount/i })) - - expect(screen.getByText('5')).toBeInTheDocument() - }) - - it('increments async', async () => { - const store = makeStore() - - render( - - - - ) - - user.type(screen.getByLabelText(/set increment amount/i), '{backspace}3') - user.click(screen.getByRole('button', { name: /add async/i })) - - await expect(screen.findByText('3')).resolves.toBeInTheDocument() - }) - - it('increments if amount is odd', async () => { - const store = makeStore() - - render( - - - - ) - - user.click(screen.getByRole('button', { name: /add if odd/i })) - - expect(screen.getByText('0')).toBeInTheDocument() - - user.click(screen.getByRole('button', { name: /increment value/i })) - user.type(screen.getByLabelText(/set increment amount/i), '{backspace}8') - user.click(screen.getByRole('button', { name: /add if odd/i })) - - await expect(screen.findByText('9')).resolves.toBeInTheDocument() - }) -}) diff --git a/src/features/counter/Counter.tsx b/src/features/counter/Counter.tsx deleted file mode 100644 index 179a547..0000000 --- a/src/features/counter/Counter.tsx +++ /dev/null @@ -1,70 +0,0 @@ -import { useState } from 'react' - -import { useAppSelector, useAppDispatch } from '../../app/hooks' -import { - decrement, - increment, - incrementByAmount, - incrementAsync, - incrementIfOdd, - selectCount, -} from './counterSlice' -import styles from './Counter.module.css' - -function Counter() { - const dispatch = useAppDispatch() - const count = useAppSelector(selectCount) - const [incrementAmount, setIncrementAmount] = useState('2') - - const incrementValue = Number(incrementAmount) || 0 - - return ( -
-
- - {count} - -
-
- setIncrementAmount(e.target.value)} - /> - - - -
-
- ) -} - -export default Counter diff --git a/src/features/counter/counterAPI.ts b/src/features/counter/counterAPI.ts deleted file mode 100644 index 9f6c4bd..0000000 --- a/src/features/counter/counterAPI.ts +++ /dev/null @@ -1,12 +0,0 @@ -export async function fetchCount(amount = 1): Promise<{ data: number }> { - const response = await fetch('/api/counter', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ amount }), - }) - const result = await response.json() - - return result -} diff --git a/src/features/counter/counterSlice.ts b/src/features/counter/counterSlice.ts deleted file mode 100644 index 5a12bd0..0000000 --- a/src/features/counter/counterSlice.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { createAsyncThunk, createSlice, PayloadAction } from '@reduxjs/toolkit' - -import type { AppState, AppThunk } from '../../app/store' -import { fetchCount } from './counterAPI' - -export interface CounterState { - value: number - status: 'idle' | 'loading' | 'failed' -} - -const initialState: CounterState = { - value: 0, - status: 'idle', -} - -// The function below is called a thunk and allows us to perform async logic. It -// can be dispatched like a regular action: `dispatch(incrementAsync(10))`. This -// will call the thunk with the `dispatch` function as the first argument. Async -// code can then be executed and other actions can be dispatched. Thunks are -// typically used to make async requests. -export const incrementAsync = createAsyncThunk( - 'counter/fetchCount', - async (amount: number) => { - const response = await fetchCount(amount) - // The value we return becomes the `fulfilled` action payload - return response.data - } -) - -export const counterSlice = createSlice({ - name: 'counter', - initialState, - // The `reducers` field lets us define reducers and generate associated actions - reducers: { - increment: (state) => { - // Redux Toolkit allows us to write "mutating" logic in reducers. It - // doesn't actually mutate the state because it uses the Immer library, - // which detects changes to a "draft state" and produces a brand new - // immutable state based off those changes - state.value += 1 - }, - decrement: (state) => { - state.value -= 1 - }, - // Use the PayloadAction type to declare the contents of `action.payload` - incrementByAmount: (state, action: PayloadAction) => { - state.value += action.payload - }, - }, - // The `extraReducers` field lets the slice handle actions defined elsewhere, - // including actions generated by createAsyncThunk or in other slices. - extraReducers: (builder) => { - builder - .addCase(incrementAsync.pending, (state) => { - state.status = 'loading' - }) - .addCase(incrementAsync.fulfilled, (state, action) => { - state.status = 'idle' - state.value += action.payload - }) - }, -}) - -export const { increment, decrement, incrementByAmount } = counterSlice.actions - -// The function below is called a selector and allows us to select a value from -// the state. Selectors can also be defined inline where they're used instead of -// in the slice file. For example: `useSelector((state: RootState) => state.counter.value)` -export const selectCount = (state: AppState) => state.counter.value - -// We can also write thunks by hand, which may contain both sync and async logic. -// Here's an example of conditionally dispatching actions based on current state. -export const incrementIfOdd = - (amount: number): AppThunk => - (dispatch, getState) => { - const currentValue = selectCount(getState()) - if (currentValue % 2 === 1) { - dispatch(incrementByAmount(amount)) - } - } - -export default counterSlice.reducer diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 2ed50fb..ffed22a 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -1,61 +1,43 @@ import type { NextPage } from 'next' import Head from 'next/head' +import { useState } from 'react' +import { EventIE, EventTypes } from '../app/types' +import { Button } from '../components/Button' +import { Checkbox } from '../components/Checkbox' +import { EventComponent } from '../components/EventComponent' +import { Input } from '../components/Input' -import Counter from '../features/counter/Counter' import styles from '../styles/Home.module.css' +const mockData = [ + { + date: new Date(2005, 11, 3), + name:"очень важная цель", + description: "очень большое и крутое описание", + type: EventTypes.AIM + } as EventIE, + { + date: new Date(2006, 1, 10), + name: "Комментарий для лоха", + description: "ты лошара тупая", + isModerated: true, + type: EventTypes.COMMENT + } as EventIE +] + + const IndexPage: NextPage = () => { + return (
Redux Toolkit + + + -
- logo - -

- Edit src/App.tsx and save to reload. -

- - Learn - - React - - , - - Redux - - , - - Redux Toolkit - - , and - - React Redux - - -
+
) } diff --git a/src/pages/registration/index.tsx b/src/pages/registration/index.tsx new file mode 100644 index 0000000..4eee36c --- /dev/null +++ b/src/pages/registration/index.tsx @@ -0,0 +1 @@ +import React from "react"; \ No newline at end of file diff --git a/src/styles/Home.module.css b/src/styles/Home.module.css index aeef564..b862009 100644 --- a/src/styles/Home.module.css +++ b/src/styles/Home.module.css @@ -1,39 +1,3 @@ -.container { - text-align: center; -} - -.logo { - height: 40vmin; - pointer-events: none; -} - -.header { - min-height: 100vh; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - font-size: calc(10px + 2vmin); -} - -.link { - color: rgb(112, 76, 182); -} - -@media (prefers-reduced-motion: no-preference) { - .logo { - animation: logo-float infinite 3s ease-in-out; - } -} - -@keyframes logo-float { - 0% { - transform: translateY(0); - } - 50% { - transform: translateY(10px); - } - 100% { - transform: translateY(0px); - } +.container{ + margin: 100px; } diff --git a/src/styles/globals.css b/src/styles/globals.css index e5e2dcc..05e6c65 100644 --- a/src/styles/globals.css +++ b/src/styles/globals.css @@ -2,8 +2,9 @@ html, body { padding: 0; margin: 0; - font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, - Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; + font-family: 'Jost', sans-serif; + font-size: 14px; + background-color: #14171b; } a { @@ -12,5 +13,7 @@ a { } * { + outline: none; box-sizing: border-box; + transition: 0.3s; } diff --git a/tsconfig.json b/tsconfig.json index 93a83a4..5bee8c4 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,7 +1,11 @@ { "compilerOptions": { "target": "es5", - "lib": ["dom", "dom.iterable", "esnext"], + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], "allowJs": true, "skipLibCheck": true, "strict": false, @@ -12,8 +16,15 @@ "moduleResolution": "node", "resolveJsonModule": true, "isolatedModules": true, - "jsx": "preserve" + "jsx": "preserve", + "incremental": true }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], - "exclude": ["node_modules"] + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx" + ], + "exclude": [ + "node_modules" + ] }