Compare commits
11 Commits
39b9f05cff
...
b1a1590c1e
| Author | SHA1 | Date |
|---|---|---|
|
|
b1a1590c1e | |
|
|
e17ac1f88f | |
|
|
5e552a439f | |
|
|
dfea794acf | |
|
|
0397efba8a | |
|
|
dd07580943 | |
|
|
829474b200 | |
|
|
ff532359f5 | |
|
|
7b97687e78 | |
|
|
55bae65444 | |
|
|
5889a81358 |
|
|
@ -18,15 +18,15 @@
|
|||
|
||||
## Programme exécutable
|
||||
|
||||
1. Téléchargez le programme exécutable pour Windows en cliquant [ici](https://github.com/polynux/groupomania-openclassrooms/releases/download/1.0/groupomania-release-1.0.zip).
|
||||
1. Téléchargez le programme exécutable pour Windows en cliquant [ici](https://github.com/polynux/groupomania-openclassrooms/releases/download/1.1/groupomania-release-1.1.zip).
|
||||
|
||||
2. Décompressez le fichier `groupomania-release-1.0.zip`.
|
||||
2. Décompressez le fichier `groupomania-release-1.1.zip`.
|
||||
|
||||
3. Lancez le programme `groupomania.exe`.
|
||||
|
||||
Si vous souhaitez compiler le programme exécutable, vous pouvez suivre les instructions suivantes :
|
||||
|
||||
Executez `cd groupomania-openclassrooms/client` puis `npm install`, puis `npm run build:neu`.
|
||||
Executez `npm run build:neu`.
|
||||
|
||||
Le programme exécutable se trouve dans le dossier `groupomania-openclassrooms/client/dist`.
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
VITE_API_URL=https://localhost:3000/api
|
||||
|
|
@ -62,6 +62,7 @@
|
|||
"cli": {
|
||||
"binaryName": "groupomania",
|
||||
"resourcesPath": "/dist-vite/",
|
||||
"clientLibrary": "/public/js/neutralino.js",
|
||||
"extensionsPath": "/extensions/",
|
||||
"binaryVersion": "4.7.0",
|
||||
"clientVersion": "3.6.0"
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -5,7 +5,7 @@ import { toastError, toastSuccess } from '@controllers/Toasts';
|
|||
import { useEffect, useState } from 'react';
|
||||
import { getMeInfo } from '@controllers/UserController';
|
||||
|
||||
const Like = ({ message }: { message: any}) => {
|
||||
const Like = ({ message }: { message: any }) => {
|
||||
const queryClient = useQueryClient();
|
||||
const [liked, setLiked] = useState(false);
|
||||
const me = useQuery(['me'], getMeInfo, {
|
||||
|
|
@ -20,7 +20,7 @@ const Like = ({ message }: { message: any}) => {
|
|||
useEffect(() => {
|
||||
if (message.likedBy.some((like: any) => like.userId === me.data?.id)) {
|
||||
setLiked(true);
|
||||
}
|
||||
}
|
||||
}, [message]);
|
||||
|
||||
const mutateLike = useMutation(liked ? unlikePost : likePost, {
|
||||
|
|
@ -28,10 +28,11 @@ const Like = ({ message }: { message: any}) => {
|
|||
queryClient.invalidateQueries(['messages']);
|
||||
if (data.message === 'Post liked') {
|
||||
setLiked(true);
|
||||
toastSuccess('Message aimé');
|
||||
} else {
|
||||
toastSuccess('Message liké');
|
||||
} else if (data.message === 'Post unliked') {
|
||||
setLiked(false);
|
||||
toastSuccess('Message non aimé');
|
||||
} else {
|
||||
toastError(data.message);
|
||||
}
|
||||
},
|
||||
onError: (error) => {
|
||||
|
|
@ -45,12 +46,16 @@ const Like = ({ message }: { message: any}) => {
|
|||
|
||||
return (
|
||||
<button
|
||||
className="absolute -bottom-10 right-0 mb-2 rounded-full bg-grey-dark shadow-lg shadow-slate-900 cursor-pointer"
|
||||
className="absolute -bottom-10 -right-2 mb-2 rounded-full bg-grey-dark shadow-lg shadow-slate-900 cursor-pointer"
|
||||
onClick={like}
|
||||
name="like"
|
||||
>
|
||||
<FaThumbsUp className={'fill-red-light text-xl w-10 h-10 p-2.5' + (liked ? ' fill-red' : '')} />
|
||||
{message.likes > 0 && <span className="absolute -top-2 right-0 text-white rounded-full bg-red w-5 h-5 text-xs text-center p-0">{message.likes}</span>}
|
||||
{message.likes > 0 && (
|
||||
<span className="absolute -top-2 right-0 text-white rounded-full bg-red w-5 h-5 text-xs text-center p-0">
|
||||
{message.likes}
|
||||
</span>
|
||||
)}
|
||||
<span className="sr-only">Aimer</span>
|
||||
</button>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ const Message = ({ message }: any) => {
|
|||
</div>
|
||||
<Text text={message.content} />
|
||||
{message.image && <Image src={message.image} alt="image" className="w-fit rounded-lg cursor-pointer" />}
|
||||
<div className="flex justify-between">
|
||||
<div className="flex justify-between gap-3 flex-wrap">
|
||||
<div className="text-grey-light date">
|
||||
{new Date(message.createdAt).toLocaleDateString(undefined, {
|
||||
year: 'numeric',
|
||||
|
|
@ -51,9 +51,10 @@ const Message = ({ message }: any) => {
|
|||
minute: 'numeric',
|
||||
})}
|
||||
</div>
|
||||
{message.edited && <div className="text-grey-light italic">Modifié</div>}
|
||||
{message.edited && <div className="text-grey-light italic flex-grow">Modifié</div>}
|
||||
{me.data?.id === message.author.id && message.likes > 0 && <div className="text-white">{message.likes} likes</div>}
|
||||
</div>
|
||||
{me.data?.id === message.author.id ? null : <Like message={message} />}
|
||||
{me.data?.id !== message.author.id && <Like message={message} />}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ const NewMessage = () => {
|
|||
)}
|
||||
<form
|
||||
onSubmit={handleSubmit}
|
||||
className="flex gap-2 flex-wrap bg-grey-dark rounded-xl p-2 mx-2 sm:p-3 md:mx-0 shadow-md shadow-grey-dark"
|
||||
className="flex gap-2 bg-grey-dark rounded-xl p-2 mx-2 sm:p-3 md:mx-0 shadow-md shadow-grey-dark"
|
||||
>
|
||||
<div className="file">
|
||||
<label htmlFor="image" className="cursor-pointer block p-2">
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
import { Cookies } from "react-cookie";
|
||||
import { api } from "../main";
|
||||
|
||||
const likePost = async (id: string) => {
|
||||
const token = new Cookies().get('token');
|
||||
const response = await fetch(`/api/posts/like/${id}`, {
|
||||
const response = await fetch(`${api}/posts/like/${id}`, {
|
||||
method: 'PUT',
|
||||
mode: 'cors',
|
||||
headers: {
|
||||
|
|
@ -19,7 +20,7 @@ const likePost = async (id: string) => {
|
|||
|
||||
const unlikePost = async (id: string) => {
|
||||
const token = new Cookies().get('token');
|
||||
const response = await fetch(`/api/posts/unlike/${id}`, {
|
||||
const response = await fetch(`${api}/posts/unlike/${id}`, {
|
||||
method: 'PUT',
|
||||
mode: 'cors',
|
||||
headers: {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
import { Cookies } from 'react-cookie';
|
||||
import { api } from '../main';
|
||||
|
||||
const getMessages = async () => {
|
||||
const token = new Cookies().get('token');
|
||||
const response = await fetch('/api/posts', {
|
||||
const response = await fetch(api + '/posts', {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
|
|
@ -17,7 +18,7 @@ const getMessages = async () => {
|
|||
|
||||
const newMessage = async (data: FormData) => {
|
||||
const token = new Cookies().get('token');
|
||||
const response = await fetch('/api/posts/new', {
|
||||
const response = await fetch(api + '/posts/new', {
|
||||
method: 'POST',
|
||||
body: data,
|
||||
mode: 'cors',
|
||||
|
|
@ -30,7 +31,7 @@ const newMessage = async (data: FormData) => {
|
|||
|
||||
const deleteMessage = async (id: string) => {
|
||||
const token = new Cookies().get('token');
|
||||
const response = await fetch(`/api/posts/delete/${id}`, {
|
||||
const response = await fetch(`${api}/posts/delete/${id}`, {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
|
|
@ -41,7 +42,7 @@ const deleteMessage = async (id: string) => {
|
|||
|
||||
const editMessage = async (id: string, data: FormData) => {
|
||||
const token = new Cookies().get('token');
|
||||
const response = await fetch(`/api/posts/edit/${id}`, {
|
||||
const response = await fetch(`${api}/posts/edit/${id}`, {
|
||||
method: 'PUT',
|
||||
body: data,
|
||||
mode: 'cors',
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
import { Cookies } from 'react-cookie';
|
||||
import { api } from '../main';
|
||||
|
||||
const getMeInfo = async () => {
|
||||
const token = new Cookies().get('token');
|
||||
|
||||
const response = await fetch('/api/me', {
|
||||
const response = await fetch(api + '/me', {
|
||||
method: 'GET',
|
||||
mode: 'cors',
|
||||
headers: {
|
||||
|
|
@ -21,7 +22,7 @@ const getMeInfo = async () => {
|
|||
const login = async ({ email, password }: { email: string; password: string }) => {
|
||||
const token = new Cookies().get('token');
|
||||
|
||||
const response = await fetch('/api/auth/login', {
|
||||
const response = await fetch(api + '/auth/login', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ email, password }),
|
||||
mode: 'cors',
|
||||
|
|
@ -51,7 +52,7 @@ const signup = async (formData: FormData) => {
|
|||
throw 'Passwords do not match';
|
||||
}
|
||||
|
||||
const response = await fetch('/api/auth/signup', {
|
||||
const response = await fetch(api + '/auth/signup', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(form),
|
||||
mode: 'cors',
|
||||
|
|
@ -69,7 +70,7 @@ const signup = async (formData: FormData) => {
|
|||
export const giveUserRights = async (userId: string, role: string) => {
|
||||
const token = new Cookies().get('token');
|
||||
|
||||
const response = await fetch(`/api/users/${userId}/roles`, {
|
||||
const response = await fetch(`${api}/users/${userId}/roles`, {
|
||||
method: 'POST',
|
||||
mode: 'cors',
|
||||
headers: {
|
||||
|
|
@ -117,7 +118,7 @@ export const changeUserInfo = async (userId: string, formData: FormData) => {
|
|||
}
|
||||
}
|
||||
|
||||
const response = await fetch(`/api/users/${userId}`, {
|
||||
const response = await fetch(`${api}/users/${userId}`, {
|
||||
method: 'PUT',
|
||||
mode: 'cors',
|
||||
headers: {
|
||||
|
|
|
|||
|
|
@ -3,3 +3,10 @@ import App from './App';
|
|||
import './index.css';
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(<App />);
|
||||
|
||||
let api = '/api';
|
||||
if (import.meta.env.VITE_API_URL) {
|
||||
api = import.meta.env.VITE_API_URL;
|
||||
}
|
||||
|
||||
export { api };
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import 'react-toastify/dist/ReactToastify.css';
|
|||
|
||||
const Home = () => {
|
||||
return (
|
||||
<div className="min-h-full max-h-full bg-grey flex flex-col items-center overflow-y-hidden">
|
||||
<div className="min-h-full max-h-full bg-grey flex flex-col items-center justify-between overflow-y-hidden">
|
||||
<AppHeader />
|
||||
<MessageWrapper />
|
||||
<NewMessage />
|
||||
|
|
|
|||
|
|
@ -5,8 +5,11 @@
|
|||
"main": "index.js",
|
||||
"scripts": {
|
||||
"dev": "nodemon --watch src -e js,ts,json --exec 'ts-node src/index.ts'",
|
||||
"build": "cd client && npm run build",
|
||||
"start": "ts-node src/index.ts",
|
||||
"build": "npm run build:server && npm run build:client",
|
||||
"build:neu": "cd client && npm run build:neu",
|
||||
"build:server": "tsc && tsc-alias",
|
||||
"build:client": "cd client && npm run build",
|
||||
"start": "node dist/index.js",
|
||||
"db": "prisma studio",
|
||||
"db:build": "prisma generate",
|
||||
"db:push": "prisma db push",
|
||||
|
|
|
|||
|
|
@ -10,8 +10,6 @@ export default async (req: Request, res: Response) => {
|
|||
}
|
||||
return res.status(200).send({ message: 'Post unliked' });
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
|
||||
return res.status(500).send(error);
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -190,11 +190,6 @@ const likePost = async (id: number, userId: number): Promise<PrismaPost | Error>
|
|||
id,
|
||||
},
|
||||
data: {
|
||||
likedBy: {
|
||||
connect: {
|
||||
id: newLike.id,
|
||||
},
|
||||
},
|
||||
likes: {
|
||||
increment: 1,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"target": "es2017",
|
||||
"target": "ES2017",
|
||||
"lib": [
|
||||
"esnext"
|
||||
],
|
||||
"moduleResolution": "node",
|
||||
"module": "CommonJS",
|
||||
"esModuleInterop": true,
|
||||
"strict": true,
|
||||
"strictNullChecks": true,
|
||||
"resolveJsonModule": true,
|
||||
"skipDefaultLibCheck": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
|
|
|
|||
Loading…
Reference in New Issue