#7 showed artists on main page

parent 3f33a793
VITE_MY_URL=http://localhost:3000
\ No newline at end of file
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React + TS</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
This source diff could not be displayed because it is too large. You can view the blob instead.
{
"name": "frontend",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview"
},
"dependencies": {
"@reduxjs/toolkit": "^1.9.3",
"axios": "^1.3.4",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-redux": "^8.0.5",
"react-router-dom": "^6.9.0"
},
"devDependencies": {
"@types/react": "^18.0.28",
"@types/react-dom": "^18.0.11",
"@vitejs/plugin-react": "^3.1.0",
"autoprefixer": "^10.4.14",
"postcss": "^8.4.21",
"tailwindcss": "^3.2.7",
"typescript": "^4.9.3",
"vite": "^4.2.0"
}
}
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
\ No newline at end of file
import React from 'react';
import HomePage from './containers/HomePage';
import {BrowserRouter, Routes, Route} from 'react-router-dom';
import Layout from './components/Layout/Layout';
import Albums from './containers/Albums';
const App = () => {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Layout />}>
<Route index element={<HomePage />} />
<Route path="albums" element={<Albums />} />
</Route>
</Routes>
</BrowserRouter>
);
};
export default App;
import {albumInstance} from './instances';
export const albumApi = {
get: async () => {
try {
const response = await albumInstance.get('');
return response.data;
} catch (err: unknown) {
console.log(err);
}
},
};
import {artistInstance} from './instances';
export const artistApi = {
get: async () => {
try {
const response = await artistInstance.get('');
return response.data;
} catch (err: unknown) {
console.log(err);
}
},
};
import axios from 'axios';
export const artistInstance = axios.create({
baseURL: `${import.meta.env.VITE_MY_URL}/artists`,
});
export const albumInstance = axios.create({
baseURL: `${import.meta.env.VITE_MY_URL}/albums`,
});
import React from 'react';
import {Outlet} from 'react-router-dom';
const Layout = () => {
return <Outlet />;
};
export default Layout;
import React, {useEffect} from 'react';
import {getArtists, selectArtists} from '../features/artist/artistSlice';
import {useAppDispatch, useAppSelector} from '../store/hooks';
import {useNavigate} from 'react-router-dom';
const HomePage = () => {
const {artists} = useAppSelector((state) => state.artist);
const dispatch = useAppDispatch();
const navigate = useNavigate();
useEffect(() => {
dispatch(getArtists());
}, [dispatch]);
return (
<div>
{artists.map((artist) => {
return (
<div key={artist._id}>
<img
onClick={() =>
navigate({pathname: '/albums', search: `?artist=${artist._id}`})
}
className="w-48 h-48 object-cover "
src={`http://localhost:3000/${artist.photo}`}
alt={artist.name}
/>
<span>{artist.name}</span>
</div>
);
})}
</div>
);
};
export default HomePage;
import {createAsyncThunk, createSlice} from '@reduxjs/toolkit';
import type {PayloadAction} from '@reduxjs/toolkit';
import {RootState} from '../../store/store';
import IAlbum from '../../interfaces/IAlbum';
import {albumApi} from '../../api/albumApi';
import axios from 'axios';
interface AlbumState {
albums: IAlbum[];
loading: boolean;
}
const initialState: AlbumState = {
albums: [],
loading: false,
};
export const getAlbums = createAsyncThunk('getAlbums', async () => {
return await albumApi.get();
});
export const getAlbumsByQueryParams = createAsyncThunk(
'getAlbumsByArtist',
async (artisId: string) => {
const res = await axios.get(`${import.meta.env.VITE_MY_URL}/albums`, {
params: {
artist: artisId,
},
});
return res.data;
}
);
export const albumSlice = createSlice({
name: 'artist',
initialState,
reducers: {},
extraReducers: (builder) => {
builder
.addCase(getAlbumsByQueryParams.pending, (state, action) => {
state.loading = true;
})
.addCase(getAlbumsByQueryParams.rejected, (state, action) => {
state.loading = false;
})
.addCase(
getAlbumsByQueryParams.fulfilled,
(state, {payload}: PayloadAction<IAlbum[]>) => {
console.log(payload);
state.albums = payload;
}
);
},
});
export const {} = albumSlice.actions;
export const selectAlbums = (state: RootState) => state.artist.artists;
export default albumSlice.reducer;
import {createAsyncThunk, createSlice} from '@reduxjs/toolkit';
import type {PayloadAction} from '@reduxjs/toolkit';
import {RootState} from '../../store/store';
import IArtist from '../../interfaces/IArtist';
import {artistApi} from '../../api/artistApi';
interface ArtistState {
artists: IArtist[];
loading: boolean;
}
const initialState: ArtistState = {
artists: [],
loading: false,
};
export const getArtists = createAsyncThunk('getArtists', async () => {
return await artistApi.get();
});
export const artistSlice = createSlice({
name: 'artist',
initialState,
reducers: {},
extraReducers: (builder) => {
builder
.addCase(getArtists.pending, (state, action) => {
state.loading = true;
})
.addCase(getArtists.rejected, (state, action) => {
state.loading = false;
})
.addCase(
getArtists.fulfilled,
(state, {payload}: PayloadAction<IArtist[]>) => {
state.artists = payload;
}
);
},
});
export const {} = artistSlice.actions;
export const selectArtists = (state: RootState) => state.artist.artists;
export default artistSlice.reducer;
@tailwind base;
@tailwind components;
@tailwind utilities;
export default interface IAlbum {
name: string;
artist: string;
year: number;
image: string;
_id: string;
}
export default interface IArtist {
name: string;
photo: string;
info: string;
_id: string;
}
import React from 'react';
import ReactDOM from 'react-dom/client';
import {Provider} from 'react-redux';
import App from './App';
import './index.css';
import {store} from './store/store';
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
<Provider store={store}>
<App />
</Provider>
);
import {useDispatch, useSelector} from 'react-redux';
import type {TypedUseSelectorHook} from 'react-redux';
import type {RootState, AppDispatch} from './store';
export const useAppDispatch: () => AppDispatch = useDispatch;
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
import {configureStore} from '@reduxjs/toolkit';
import albumSlice from '../features/album/albumSlice';
import artistSlice from '../features/artist/artistSlice';
export const store = configureStore({
reducer: {
artist: artistSlice,
album: albumSlice,
},
});
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
/// <reference types="vite/client" />
interface ImportMetaEnv {
readonly VITE_MY_URL: string;
}
interface ImportMeta {
readonly env: ImportMetaEnv;
}
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'],
theme: {
extend: {},
},
plugins: [],
};
{
"compilerOptions": {
"target": "ESNext",
"useDefineForClassFields": true,
"lib": ["DOM", "DOM.Iterable", "ESNext"],
"allowJs": false,
"skipLibCheck": true,
"esModuleInterop": false,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "ESNext",
"moduleResolution": "Node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx"
},
"include": ["src"],
"references": [{ "path": "./tsconfig.node.json" }]
}
{
"compilerOptions": {
"composite": true,
"module": "ESNext",
"moduleResolution": "Node",
"allowSyntheticDefaultImports": true
},
"include": ["vite.config.ts"]
}
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
})
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment