Skip to content

Commit

Permalink
UPDATE: Using RESTful API
Browse files Browse the repository at this point in the history
  • Loading branch information
WCY-dt committed May 1, 2024
1 parent 24478b7 commit 7bc78f4
Show file tree
Hide file tree
Showing 18 changed files with 264 additions and 76 deletions.
12 changes: 12 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# http://editorconfig.org
root = true

[*]
indent_style = tab
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

[*.yml]
indent_style = space
32 changes: 27 additions & 5 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "mindclip",
"version": "2.0.0",
"version": "2.0.1",
"private": true,
"author": "ch3nyang",
"license": "GPL-3.0-only",
Expand Down Expand Up @@ -37,7 +37,9 @@
"@iconify/react": "^4.1.1",
"@types/react": "^18.3.0",
"@types/react-dom": "^18.3.0",
"@types/uuid": "^9.0.8",
"cross-env": "^7.0.3",
"typescript": "^4.9.5"
"typescript": "^4.9.5",
"uuid": "^9.0.1"
}
}
10 changes: 9 additions & 1 deletion src/components/clusters.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,13 @@ interface ClustersProps {
dataKey: string;
searchTerm: string;
setSearchTerm: (value: string) => void;
isLogedIn: boolean;
token: string;
message: string | null;
setMessage: (value: string | null) => void;
}

function Clusters({ dataKey, searchTerm, setSearchTerm }: ClustersProps) {
function Clusters({ dataKey, searchTerm, setSearchTerm, isLogedIn, token, message, setMessage }: ClustersProps) {
const [clusters, setClusters] = useState<ClusterProps[]>([]);
const [selectedCategory, setSelectedCategory] = useState<string | null>(null);
const [isLoading, setIsLoading] = useState<boolean>(true);
Expand Down Expand Up @@ -39,6 +43,10 @@ function Clusters({ dataKey, searchTerm, setSearchTerm }: ClustersProps) {
key={cluster.Title}
item={cluster}
setSelectedCategory={setSelectedCategory}
isLogedIn={isLogedIn}
token={token}
message={message}
setMessage={setMessage}
/>
))
) : (
Expand Down
8 changes: 5 additions & 3 deletions src/components/content.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,20 @@ interface ContentProps {
isLogedIn: boolean;
token: string;
setToken: (value: string) => void;
message: string | null;
setMessage: (value: string | null) => void;
}

function Content({ routes, searchTerm, setSearchTerm, isLogedIn, token, setToken }: ContentProps) {
function Content({ routes, searchTerm, setSearchTerm, isLogedIn, token, setToken, message, setMessage }: ContentProps) {
return (
<div className="App-content">
<Routes>
<Route path="/"
element={<Cluster dataKey={routes[Object.keys(routes)[0]]} searchTerm={searchTerm} setSearchTerm={setSearchTerm} />}
element={<Cluster dataKey={routes[Object.keys(routes)[0]]} searchTerm={searchTerm} setSearchTerm={setSearchTerm} isLogedIn={isLogedIn} token={token} message={message} setMessage={setMessage} />}
/>
{Object.entries(routes).map(([path, element]) => (
<Route path={path}
element={<Cluster dataKey={element} searchTerm={searchTerm} setSearchTerm={setSearchTerm} />}
element={<Cluster dataKey={element} searchTerm={searchTerm} setSearchTerm={setSearchTerm} isLogedIn={isLogedIn} token={token} message={message} setMessage={setMessage} />}
/>
))}
</Routes>
Expand Down
4 changes: 2 additions & 2 deletions src/components/header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,11 @@ function Header({ routes, searchTerm, setSearchTerm, isLogedIn, setIsLogedIn, to
</div>
<button className="App-nav-button" onClick={toggleMenu}>{isOpen ? <Icon icon="ci:window-close" /> : <Icon icon="ci:window-sidebar" />}</button>
{isOpen && (
<div className="App-nav-menu">
<ul className="App-nav-menu">
{Object.entries(routes).map(([path, element]) => (
<li className="App-nav-item"><Link to={path}>{element}</Link></li>
))}
</div>
</ul>
)}
</nav>
<div className={`overlay ${(isOpen || showLogin) ? 'open' : ''}`} onClick={() => {
Expand Down
134 changes: 84 additions & 50 deletions src/components/linkCard.tsx
Original file line number Diff line number Diff line change
@@ -1,71 +1,105 @@
import React from 'react';
import React, { useState } from 'react';
import { Icon } from '@iconify/react';
import { v4 as uuidv4 } from 'uuid';
import { getRandomColor, getContrastColor } from '../utils/randomColor';
import Confirm from '../popups/confirm';
import deleteCardHandler from '../services/deleteCardHandler';
import '../styles/linkCard.css';

interface LinkCardProps {
item: ClusterProps;
setSelectedCategory: (value: string) => void;
isLogedIn: boolean;
token: string;
message: string | null;
setMessage: (value: string | null) => void;
}

function LinkCard({ item, setSelectedCategory }: LinkCardProps) {
function LinkCard({ item, setSelectedCategory, isLogedIn, token, message, setMessage }: LinkCardProps) {
const backgroundColor = getRandomColor(item.Category);
const textColor = getContrastColor(backgroundColor);

const handleCategoryClick = (e: React.MouseEvent) => {
e.preventDefault();
setSelectedCategory(item.Category);
};
e.preventDefault();
setSelectedCategory(item.Category);
};

const [showConfirm, setShowConfirm] = useState<boolean>(false);
const confirmMessage = "Are you confirmed to delete this card?";

const tryDeleteCard = async () => {
const id = item.Id || 0;
const deleteCardResult = await deleteCardHandler({ id, token });

if (deleteCardResult === true) {
setMessage('Successfully deleted');
} else {
setMessage('Failed to delete. Please try again.');
}
};

return (
<a
className="link-card"
href={item.Url ? item.Url.replace(/&#39;/g, "'") : '#'}
target="_blank"
rel="noopener noreferrer"
id={item.Title}
>
<div className="link-card-left">
<div className="link-card-category" style={{ backgroundColor, color: textColor }} onClick={handleCategoryClick}>
{item.Category ? item.Category.replace(/&#39;/g, "'") : 'UNKNOWN'}
</div>
<div className="link-card-icon">
<img
src={`https://www.google.com/s2/favicons?sz=64&domain=${item.Url ? new URL(item.Url.replace(/&#39;/g, "'")).hostname : 'ch3nyang.top'}`}
alt="favicon"
className="link-card-icon-img"
/>
</div>
</div>

<div className="link-card-right">
<div className="link-card-title">
{item.Title.replace(/&#39;/g, "'")}
</div>
<div className="link-card-description">
{item.Description.replace(/&#39;/g, "'")}
<>
<div className="link-card">
<div className="link-card-left">
<div className="link-card-info">
<div className="link-card-category" style={{ backgroundColor, color: textColor }} onClick={handleCategoryClick}>
{item.Category ? item.Category : 'UNKNOWN'}
</div>
<div className="link-card-icon">
<img
src={`https://www.google.com/s2/favicons?sz=64&domain=${item.Url ? new URL(item.Url).hostname : 'ch3nyang.top'}`}
alt="favicon"
className="link-card-icon-img"
/>
</div>
</div>
{isLogedIn ? (
<div className="link-card-edit">
<Icon icon="ci:edit-pencil-line-01" className="link-card-edit-edit"></Icon>
<Icon icon="ci:trash-full" className="link-card-edit-delete" onClick={() => setShowConfirm(true)}></Icon>
</div>
) : null
}
</div>
{item.Detail ? (
<div className="link-card-detail">
{item.Detail.replace(/&#39;/g, "'")}

<a
className="link-card-right"
href={item.Url ? item.Url : '#'}
target="_blank"
rel="noopener noreferrer"
id={uuidv4()}
>
<div className="link-card-title">
{item.Title}
</div>
) : null}
{item.links ? (
<div className="link-card-link">
{item.links && item.links.length > 0 ? item.links.map((link) => (
<a
key={link.Url}
href={link.Url.replace(/&#39;/g, "'")}
target="_blank"
rel="noopener noreferrer"
className="link-card-link-item"
>
{link.Title.replace(/&#39;/g, "'")}
</a>
)) : ''}
<div className="link-card-description">
{item.Description}
</div>
) : null}
{item.Detail ? (
<div className="link-card-detail">
{item.Detail}
</div>
) : null}
{(item.links && item.links.length > 0) ? (
<div className="link-card-link">
{item.links.map((link) => (
<a
key={link.Url}
href={link.Url}
target="_blank"
rel="noopener noreferrer"
className="link-card-link-item"
>
{link.Title}
</a>
))}
</div>
) : null}
</a>
</div>
</a>
{showConfirm ? (<Confirm message={confirmMessage} setShowConfirm={setShowConfirm} actionHandler={tryDeleteCard}/>) : null}
</>
);
}

Expand Down
28 changes: 28 additions & 0 deletions src/popups/confirm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import React from 'react';
import '../styles/popup.css';
import '../styles/confirm.css';

interface ConfirmProps {
message: string | null;
setShowConfirm: (value: boolean) => void;
actionHandler: () => void;
}

function Confirm({ message, setShowConfirm, actionHandler }: ConfirmProps) {
return (
<>
<div className="Popup">
<div className="Confirm-message">{ message }</div>
<div className="Confirm-button">
<button type="button" className="Confirm-button-cancel" onClick={() => {setShowConfirm(false);}}>cancel</button>
<button type="button" className="Confirm-button-ok" onClick={() => {
actionHandler();
setShowConfirm(false);
}} >confirm</button>
</div>
</div>
</>
);
}

export default Confirm;
4 changes: 1 addition & 3 deletions src/popups/login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,13 @@ function Login({ setShowLogin, setIsLogedIn, token, setToken, message, setMessag
const password = (document.getElementById("password") as HTMLInputElement).value;
const loginResult = await loginHandler({ username, password, setToken });

console.log(loginResult);

if (loginResult === true) {
setShowLogin(false);
localStorage.setItem('token', token);
setIsLogedIn(true);
setMessage('Successfully logged in');
} else {
alert('Failed to login. Please try again.');
setMessage('Login failed. Please try again.');
}
};

Expand Down
2 changes: 1 addition & 1 deletion src/routers/router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ function Router() {
<BrowserRouter>
<Header routes={routes} searchTerm={searchTerm} setSearchTerm={setSearchTerm} isLogedIn={isLogedIn} setIsLogedIn={setIsLogedIn} token={token} setToken={setToken} message={message} setMessage={setMessage} />

<Content routes={routes} searchTerm={searchTerm} setSearchTerm={setSearchTerm} isLogedIn={isLogedIn} token={token} setToken={setToken}/>
<Content routes={routes} searchTerm={searchTerm} setSearchTerm={setSearchTerm} isLogedIn={isLogedIn} token={token} setToken={setToken} message={message} setMessage={setMessage} />

<Footer />
</BrowserRouter>
Expand Down
2 changes: 1 addition & 1 deletion src/services/collectionFetcher.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
export function fetchCollection(setRoutes: (value: {}) => void) {
return new Promise(async (resolve, reject) => {
const api = process.env.REACT_APP_API_URL;
let url = `${api}/collection`;
let url = `${api}/card/collection`;

try {
const response = await fetch(url, { method: 'GET' });
Expand Down
Loading

0 comments on commit 7bc78f4

Please sign in to comment.