mirror of
https://github.com/aykhans/slash-e.git
synced 2025-04-17 20:55:28 +00:00
chore: update frontend actions
This commit is contained in:
parent
fbe09d7cd0
commit
840659e2b2
@ -66,24 +66,26 @@ const CreateShortcutDialog: React.FC<Props> = (props: Props) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="w-full flex flex-row justify-between items-center mb-4">
|
<div className="max-w-full w-80 flex flex-row justify-between items-center mb-4">
|
||||||
<p className="text-base">{shortcutId ? "Edit Shortcut" : "Create Shortcut"}</p>
|
<p className="text-base">{shortcutId ? "Edit Shortcut" : "Create Shortcut"}</p>
|
||||||
<button className="rounded p-1 hover:bg-gray-100" onClick={destroy}>
|
<button className="rounded p-1 hover:bg-gray-100" onClick={destroy}>
|
||||||
<Icon.X className="w-5 h-auto text-gray-600" />
|
<Icon.X className="w-5 h-auto text-gray-600" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full flex flex-col justify-start items-start">
|
<div className="w-full flex flex-col justify-start items-start">
|
||||||
<div className="w-full flex flex-row justify-start items-center mb-2">
|
<div className="w-full flex flex-col justify-start items-start mb-3">
|
||||||
<input className="rounded border px-2 py-2" type="text" placeholder="Name" value={name} onChange={handleNameInputChange} />
|
<span className="mb-2">Name</span>
|
||||||
|
<input className="w-full rounded border px-2 py-2" type="text" value={name} onChange={handleNameInputChange} />
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full flex flex-row justify-start items-center mb-2">
|
<div className="w-full flex flex-col justify-start items-start mb-3">
|
||||||
<input className="rounded border px-2 py-2" type="text" placeholder="Link" value={link} onChange={handleLinkInputChange} />
|
<span className="mb-2">Link</span>
|
||||||
|
<input className="w-full rounded border px-2 py-2" type="text" value={link} onChange={handleLinkInputChange} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div className="w-full flex flex-row justify-end items-center">
|
||||||
<div className="w-full flex flex-row justify-end items-center mt-2">
|
|
||||||
<div className="flex flex-row justify-start items-center">
|
|
||||||
<button
|
<button
|
||||||
className={`border rounded px-2 py-1 border-green-600 text-green-600 ${requestState.isLoading ? "opacity-80" : ""}`}
|
className={`border rounded px-3 py-2 border-green-600 bg-green-600 text-white hover:bg-green-700 ${
|
||||||
|
requestState.isLoading ? "opacity-80" : ""
|
||||||
|
}`}
|
||||||
onClick={handleSaveBtnClick}
|
onClick={handleSaveBtnClick}
|
||||||
>
|
>
|
||||||
Save
|
Save
|
||||||
|
@ -63,30 +63,26 @@ const CreateWorkspaceDialog: React.FC<Props> = (props: Props) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="w-full flex flex-row justify-between items-center mb-4">
|
<div className="max-w-full w-80 flex flex-row justify-between items-center mb-4">
|
||||||
<p className="text-base">{workspaceId ? "Edit Workspace" : "Create Workspace"}</p>
|
<p className="text-base">{workspaceId ? "Edit Workspace" : "Create Workspace"}</p>
|
||||||
<button className="rounded p-1 hover:bg-gray-100" onClick={destroy}>
|
<button className="rounded p-1 hover:bg-gray-100" onClick={destroy}>
|
||||||
<Icon.X className="w-5 h-auto text-gray-600" />
|
<Icon.X className="w-5 h-auto text-gray-600" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full flex flex-col justify-start items-start">
|
<div className="w-full flex flex-col justify-start items-start">
|
||||||
<div className="w-full flex flex-row justify-start items-center mb-2">
|
<div className="w-full flex flex-col justify-start items-start mb-3">
|
||||||
<input className="rounded border px-2 py-2" type="text" placeholder="Name" value={name} onChange={handleNameInputChange} />
|
<span className="mb-2">Name</span>
|
||||||
|
<input className="w-full rounded border px-2 py-2" type="text" value={name} onChange={handleNameInputChange} />
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full flex flex-row justify-start items-center mb-2">
|
<div className="w-full flex flex-col justify-start items-start mb-3">
|
||||||
<input
|
<span className="mb-2">Description</span>
|
||||||
className="rounded border px-2 py-2"
|
<input className="w-full rounded border px-2 py-2" type="text" value={description} onChange={handleDescriptionInputChange} />
|
||||||
type="text"
|
|
||||||
placeholder="Description"
|
|
||||||
value={description}
|
|
||||||
onChange={handleDescriptionInputChange}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div className="w-full flex flex-row justify-end items-center">
|
||||||
<div className="w-full flex flex-row justify-end items-center mt-2">
|
|
||||||
<div className="flex flex-row justify-start items-center">
|
|
||||||
<button
|
<button
|
||||||
className={`border rounded px-2 py-1 border-green-600 text-green-600 ${requestState.isLoading ? "opacity-80" : ""}`}
|
className={`border rounded px-3 py-2 border-green-600 bg-green-600 text-white hover:bg-green-700 ${
|
||||||
|
requestState.isLoading ? "opacity-80" : ""
|
||||||
|
}`}
|
||||||
onClick={handleSaveBtnClick}
|
onClick={handleSaveBtnClick}
|
||||||
>
|
>
|
||||||
Save
|
Save
|
||||||
|
@ -3,7 +3,6 @@ import { useAppSelector } from "../store";
|
|||||||
import { userService } from "../services";
|
import { userService } from "../services";
|
||||||
import useToggle from "../hooks/useToggle";
|
import useToggle from "../hooks/useToggle";
|
||||||
import Icon from "./Icon";
|
import Icon from "./Icon";
|
||||||
import styles from "../less/header.module.less";
|
|
||||||
|
|
||||||
const Header: React.FC = () => {
|
const Header: React.FC = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@ -16,9 +15,9 @@ const Header: React.FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.header}>
|
<div className="w-full bg-amber-50">
|
||||||
<div className="w-full max-w-4xl mx-auto px-3 py-4 flex flex-row justify-between items-center">
|
<div className="w-full max-w-4xl mx-auto px-3 py-4 flex flex-row justify-between items-center">
|
||||||
<span>Corgi</span>
|
<span className="text-xl font-mono font-medium">Corgi</span>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<div className="flex flex-row justify-end items-center" onClick={() => toggleShowDropdown()}>
|
<div className="flex flex-row justify-end items-center" onClick={() => toggleShowDropdown()}>
|
||||||
<span>{user?.name}</span>
|
<span>{user?.name}</span>
|
||||||
|
@ -1,17 +1,44 @@
|
|||||||
|
import { shortcutService } from "../services";
|
||||||
|
import Dropdown from "./common/Dropdown";
|
||||||
|
import showCreateShortcutDialog from "./CreateShortcutDialog";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
workspaceId: WorkspaceId;
|
||||||
shortcutList: Shortcut[];
|
shortcutList: Shortcut[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const ShortcutListView: React.FC<Props> = (props: Props) => {
|
const ShortcutListView: React.FC<Props> = (props: Props) => {
|
||||||
const { shortcutList } = props;
|
const { workspaceId, shortcutList } = props;
|
||||||
|
|
||||||
|
const handleDeleteShortcutButtonClick = (shortcut: Shortcut) => {
|
||||||
|
shortcutService.deleteShortcutById(shortcut.id);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full flex flex-col justify-start items-start">
|
<div className="w-full flex flex-col justify-start items-start">
|
||||||
{shortcutList.map((shortcut) => {
|
{shortcutList.map((shortcut) => {
|
||||||
return (
|
return (
|
||||||
<div key={shortcut.id} className="w-full flex flex-col justify-start items-start border px-6 py-4 mb-2 rounded-lg">
|
<div key={shortcut.id} className="w-full flex flex-row justify-between items-start border px-6 py-4 mb-3 rounded-lg">
|
||||||
<span className="text-xl font-medium">{shortcut.name}</span>
|
<div className="flex flex-col justify-start items-start">
|
||||||
<span className="text-base text-gray-600">{shortcut.link}</span>
|
<span className="text-lg font-medium">{shortcut.name}</span>
|
||||||
|
<span className="text-base text-gray-600">{shortcut.link}</span>
|
||||||
|
</div>
|
||||||
|
<Dropdown>
|
||||||
|
<span
|
||||||
|
className="w-full px-2 leading-8 cursor-pointer rounded hover:bg-gray-100"
|
||||||
|
onClick={() => showCreateShortcutDialog(workspaceId, shortcut.id)}
|
||||||
|
>
|
||||||
|
Edit
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
className="w-full px-2 leading-8 cursor-pointer rounded text-red-600 hover:bg-gray-100"
|
||||||
|
onClick={() => {
|
||||||
|
handleDeleteShortcutButtonClick(shortcut);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Delete
|
||||||
|
</span>
|
||||||
|
</Dropdown>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
@ -1,4 +1,8 @@
|
|||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
|
import { workspaceService } from "../services";
|
||||||
|
import workspace from "../store/modules/workspace";
|
||||||
|
import Dropdown from "./common/Dropdown";
|
||||||
|
import showCreateWorkspaceDialog from "./CreateWorkspaceDialog";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
workspaceList: Workspace[];
|
workspaceList: Workspace[];
|
||||||
@ -12,15 +16,37 @@ const WorkspaceListView: React.FC<Props> = (props: Props) => {
|
|||||||
navigate(`/workspace/${workspace.id}`);
|
navigate(`/workspace/${workspace.id}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleDeleteWorkspaceButtonClick = (workspace: Workspace) => {
|
||||||
|
workspaceService.deleteWorkspaceById(workspace.id);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full flex flex-col justify-start items-start">
|
<div className="w-full flex flex-col justify-start items-start">
|
||||||
{workspaceList.map((workspace) => {
|
{workspaceList.map((workspace) => {
|
||||||
return (
|
return (
|
||||||
<div key={workspace.id} className="w-full flex flex-col justify-start items-start border px-6 py-4 mb-2 rounded-lg">
|
<div key={workspace.id} className="w-full flex flex-row justify-between items-start border px-6 py-4 mb-3 rounded-lg">
|
||||||
<span className="text-xl font-medium" onClick={() => gotoWorkspaceDetailPage(workspace)}>
|
<div className="flex flex-col justify-start items-start">
|
||||||
{workspace.name}
|
<span className="text-lg font-medium cursor-pointer hover:underline" onClick={() => gotoWorkspaceDetailPage(workspace)}>
|
||||||
</span>
|
{workspace.name}
|
||||||
<span className="text-base text-gray-600">{workspace.description}</span>
|
</span>
|
||||||
|
<span className="text-base text-gray-600">{workspace.description}</span>
|
||||||
|
</div>
|
||||||
|
<Dropdown>
|
||||||
|
<span
|
||||||
|
className="w-full px-2 leading-8 cursor-pointer rounded hover:bg-gray-100"
|
||||||
|
onClick={() => showCreateWorkspaceDialog(workspace.id)}
|
||||||
|
>
|
||||||
|
Edit
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
className="w-full px-2 leading-8 cursor-pointer rounded text-red-600 hover:bg-gray-100"
|
||||||
|
onClick={() => {
|
||||||
|
handleDeleteWorkspaceButtonClick(workspace);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Delete
|
||||||
|
</span>
|
||||||
|
</Dropdown>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
@ -1,120 +0,0 @@
|
|||||||
import { useEffect, useState } from "react";
|
|
||||||
import { DAILY_TIMESTAMP } from "../../helpers/consts";
|
|
||||||
import Icon from "../Icon";
|
|
||||||
import "../../less/common/date-picker.less";
|
|
||||||
|
|
||||||
interface DatePickerProps {
|
|
||||||
className?: string;
|
|
||||||
datestamp: DateStamp;
|
|
||||||
handleDateStampChange: (datastamp: DateStamp) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const DatePicker: React.FC<DatePickerProps> = (props: DatePickerProps) => {
|
|
||||||
const { className, datestamp, handleDateStampChange } = props;
|
|
||||||
const [currentDateStamp, setCurrentDateStamp] = useState<DateStamp>(getMonthFirstDayDateStamp(datestamp));
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setCurrentDateStamp(getMonthFirstDayDateStamp(datestamp));
|
|
||||||
}, [datestamp]);
|
|
||||||
|
|
||||||
const firstDate = new Date(currentDateStamp);
|
|
||||||
const firstDateDay = firstDate.getDay() === 0 ? 7 : firstDate.getDay();
|
|
||||||
const dayList = [];
|
|
||||||
for (let i = 1; i < firstDateDay; i++) {
|
|
||||||
dayList.push({
|
|
||||||
date: 0,
|
|
||||||
datestamp: firstDate.getTime() - DAILY_TIMESTAMP * (7 - i),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
const dayAmount = getMonthDayAmount(currentDateStamp);
|
|
||||||
for (let i = 1; i <= dayAmount; i++) {
|
|
||||||
dayList.push({
|
|
||||||
date: i,
|
|
||||||
datestamp: firstDate.getTime() + DAILY_TIMESTAMP * (i - 1),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleDateItemClick = (datestamp: DateStamp) => {
|
|
||||||
handleDateStampChange(datestamp);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleChangeMonthBtnClick = (i: -1 | 1) => {
|
|
||||||
const year = firstDate.getFullYear();
|
|
||||||
const month = firstDate.getMonth() + 1;
|
|
||||||
let nextDateStamp = 0;
|
|
||||||
if (month === 1 && i === -1) {
|
|
||||||
nextDateStamp = new Date(`${year - 1}/12/1`).getTime();
|
|
||||||
} else if (month === 12 && i === 1) {
|
|
||||||
nextDateStamp = new Date(`${year + 1}/1/1`).getTime();
|
|
||||||
} else {
|
|
||||||
nextDateStamp = new Date(`${year}/${month + i}/1`).getTime();
|
|
||||||
}
|
|
||||||
setCurrentDateStamp(getMonthFirstDayDateStamp(nextDateStamp));
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={`date-picker-wrapper ${className}`}>
|
|
||||||
<div className="date-picker-header">
|
|
||||||
<span className="btn-text" onClick={() => handleChangeMonthBtnClick(-1)}>
|
|
||||||
<Icon.ChevronLeft className="icon-img" />
|
|
||||||
</span>
|
|
||||||
<span className="normal-text">
|
|
||||||
{firstDate.getFullYear()}/{firstDate.getMonth() + 1}
|
|
||||||
</span>
|
|
||||||
<span className="btn-text" onClick={() => handleChangeMonthBtnClick(1)}>
|
|
||||||
<Icon.ChevronRight className="icon-img" />
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div className="date-picker-day-container">
|
|
||||||
<div className="date-picker-day-header">
|
|
||||||
<span className="day-item">Mon</span>
|
|
||||||
<span className="day-item">Tue</span>
|
|
||||||
<span className="day-item">Web</span>
|
|
||||||
<span className="day-item">Thu</span>
|
|
||||||
<span className="day-item">Fri</span>
|
|
||||||
<span className="day-item">Sat</span>
|
|
||||||
<span className="day-item">Sun</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{dayList.map((d) => {
|
|
||||||
if (d.date === 0) {
|
|
||||||
return (
|
|
||||||
<span key={d.datestamp} className="day-item null">
|
|
||||||
{""}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
<span
|
|
||||||
key={d.datestamp}
|
|
||||||
className={`day-item ${d.datestamp === datestamp ? "current" : ""}`}
|
|
||||||
onClick={() => handleDateItemClick(d.datestamp)}
|
|
||||||
>
|
|
||||||
{d.date}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
function getMonthDayAmount(datestamp: DateStamp): number {
|
|
||||||
const dateTemp = new Date(datestamp);
|
|
||||||
const currentDate = new Date(`${dateTemp.getFullYear()}/${dateTemp.getMonth() + 1}/1`);
|
|
||||||
const nextMonthDate =
|
|
||||||
currentDate.getMonth() === 11
|
|
||||||
? new Date(`${currentDate.getFullYear() + 1}/1/1`)
|
|
||||||
: new Date(`${currentDate.getFullYear()}/${currentDate.getMonth() + 2}/1`);
|
|
||||||
|
|
||||||
return (nextMonthDate.getTime() - currentDate.getTime()) / DAILY_TIMESTAMP;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getMonthFirstDayDateStamp(timestamp: TimeStamp): DateStamp {
|
|
||||||
const dateTemp = new Date(timestamp);
|
|
||||||
const currentDate = new Date(`${dateTemp.getFullYear()}/${dateTemp.getMonth() + 1}/1`);
|
|
||||||
return currentDate.getTime();
|
|
||||||
}
|
|
||||||
|
|
||||||
export default DatePicker;
|
|
@ -1,103 +0,0 @@
|
|||||||
.page-wrapper.auth {
|
|
||||||
@apply flex flex-row justify-center items-center w-full h-screen bg-white;
|
|
||||||
|
|
||||||
> .page-container {
|
|
||||||
@apply w-80 max-w-full h-full py-4 flex flex-col justify-start items-center;
|
|
||||||
|
|
||||||
> .auth-form-wrapper {
|
|
||||||
@apply w-full py-4 grow flex flex-col justify-center items-center;
|
|
||||||
|
|
||||||
> .page-header-container {
|
|
||||||
@apply flex flex-col justify-start items-start w-full mb-4;
|
|
||||||
|
|
||||||
> .title-container {
|
|
||||||
@apply w-full flex flex-row justify-between items-center;
|
|
||||||
|
|
||||||
> .logo-img {
|
|
||||||
@apply h-20 w-auto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
> .slogan-text {
|
|
||||||
@apply text-sm text-gray-700;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
> .page-content-container {
|
|
||||||
@apply flex flex-col justify-start items-start w-full;
|
|
||||||
|
|
||||||
> .form-item-container {
|
|
||||||
@apply flex flex-col justify-start items-start relative w-full text-base mt-2;
|
|
||||||
|
|
||||||
> .normal-text {
|
|
||||||
@apply absolute top-3 left-3 px-1 leading-10 flex-shrink-0 text-base cursor-text text-gray-400 bg-transparent transition-all select-none;
|
|
||||||
|
|
||||||
&.not-null {
|
|
||||||
@apply text-sm top-0 z-10 leading-4 bg-white rounded;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.input-form-container {
|
|
||||||
@apply py-2;
|
|
||||||
|
|
||||||
> input {
|
|
||||||
@apply w-full py-3 px-3 text-base shadow-inner rounded-lg border border-solid border-gray-400 hover:opacity-80;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.requesting {
|
|
||||||
@apply opacity-80;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
> .action-btns-container {
|
|
||||||
@apply flex flex-row justify-end items-center w-full mt-2;
|
|
||||||
|
|
||||||
> .btn {
|
|
||||||
@apply flex flex-row justify-center items-center px-1 py-2 text-sm rounded hover:opacity-80;
|
|
||||||
|
|
||||||
&.signin-btn {
|
|
||||||
@apply bg-green-600 text-white px-3 shadow;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.requesting {
|
|
||||||
@apply cursor-wait opacity-80;
|
|
||||||
}
|
|
||||||
|
|
||||||
> .img-icon {
|
|
||||||
@apply w-4 h-auto mr-1 animate-spin;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
> .tip-text {
|
|
||||||
@apply w-full inline-block float-right text-sm mt-4 text-gray-500 text-right whitespace-pre-wrap;
|
|
||||||
|
|
||||||
&.host-tip {
|
|
||||||
@apply text-blue-600;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
> .footer-container {
|
|
||||||
@apply w-full flex flex-col justify-start items-center;
|
|
||||||
|
|
||||||
> .language-container {
|
|
||||||
@apply mt-2 w-full flex flex-row justify-center items-center text-sm text-gray-400;
|
|
||||||
|
|
||||||
> .locale-item {
|
|
||||||
@apply px-2 cursor-pointer;
|
|
||||||
|
|
||||||
&.active {
|
|
||||||
@apply text-blue-600 font-bold;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
> .split-line {
|
|
||||||
@apply font-mono text-gray-400;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,65 +0,0 @@
|
|||||||
@import "../mixin.less";
|
|
||||||
|
|
||||||
.date-picker-wrapper {
|
|
||||||
@apply flex flex-col justify-start items-start p-4;
|
|
||||||
|
|
||||||
> .date-picker-header {
|
|
||||||
@apply flex flex-row justify-center items-center w-full mb-2;
|
|
||||||
|
|
||||||
> .btn-text {
|
|
||||||
@apply w-6 h-6 rounded cursor-pointer select-none flex flex-col justify-center items-center opacity-40 hover:bg-gray-200;
|
|
||||||
}
|
|
||||||
|
|
||||||
> .normal-text {
|
|
||||||
@apply mx-1 leading-6 font-mono;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
> .date-picker-day-container {
|
|
||||||
.flex(row, flex-start, flex-start);
|
|
||||||
width: 280px;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
|
|
||||||
> .date-picker-day-header {
|
|
||||||
.flex(row, space-around, center);
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
> .day-item {
|
|
||||||
.flex(column, center, center);
|
|
||||||
width: 36px;
|
|
||||||
height: 36px;
|
|
||||||
user-select: none;
|
|
||||||
color: gray;
|
|
||||||
font-size: 13px;
|
|
||||||
margin: 2px 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
> .day-item {
|
|
||||||
.flex(column, center, center);
|
|
||||||
width: 36px;
|
|
||||||
height: 36px;
|
|
||||||
border-radius: 50%;
|
|
||||||
font-size: 14px;
|
|
||||||
user-select: none;
|
|
||||||
cursor: pointer;
|
|
||||||
margin: 2px;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background-color: @bg-whitegray;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.current {
|
|
||||||
background-color: @bg-light-blue;
|
|
||||||
font-size: 16px;
|
|
||||||
color: @text-blue;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.null {
|
|
||||||
background-color: unset;
|
|
||||||
cursor: unset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,5 +1,3 @@
|
|||||||
@import "../mixin.less";
|
|
||||||
|
|
||||||
.dropdown-wrapper {
|
.dropdown-wrapper {
|
||||||
@apply relative flex flex-col justify-start items-start select-none;
|
@apply relative flex flex-col justify-start items-start select-none;
|
||||||
|
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
@import "../mixin.less";
|
|
||||||
|
|
||||||
.selector-wrapper {
|
.selector-wrapper {
|
||||||
@apply flex flex-col justify-start items-start relative h-8;
|
@apply flex flex-col justify-start items-start relative h-8;
|
||||||
|
|
||||||
@ -30,7 +28,6 @@
|
|||||||
min-width: calc(100% + 16px);
|
min-width: calc(100% + 16px);
|
||||||
max-height: 256px;
|
max-height: 256px;
|
||||||
box-shadow: 0 0 8px 0 rgb(0 0 0 / 20%);
|
box-shadow: 0 0 8px 0 rgb(0 0 0 / 20%);
|
||||||
.hide-scroll-bar();
|
|
||||||
|
|
||||||
> .item-container {
|
> .item-container {
|
||||||
@apply flex flex-col justify-start items-start w-full px-3 text-sm select-none leading-8 cursor-pointer rounded whitespace-nowrap hover:bg-gray-100;
|
@apply flex flex-col justify-start items-start w-full px-3 text-sm select-none leading-8 cursor-pointer rounded whitespace-nowrap hover:bg-gray-100;
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
.header {
|
|
||||||
@apply w-full bg-amber-50;
|
|
||||||
}
|
|
@ -7,7 +7,6 @@ import useLoading from "../hooks/useLoading";
|
|||||||
import Icon from "../components/Icon";
|
import Icon from "../components/Icon";
|
||||||
import Only from "../components/common/OnlyWhen";
|
import Only from "../components/common/OnlyWhen";
|
||||||
import toastHelper from "../components/Toast";
|
import toastHelper from "../components/Toast";
|
||||||
import "../less/auth.less";
|
|
||||||
|
|
||||||
const validateConfig: ValidatorConfig = {
|
const validateConfig: ValidatorConfig = {
|
||||||
minLength: 4,
|
minLength: 4,
|
||||||
@ -73,7 +72,42 @@ const Auth: React.FC = () => {
|
|||||||
replace: true,
|
replace: true,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
toastHelper.error("Login failed");
|
toastHelper.error("Signin failed");
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error(error);
|
||||||
|
toastHelper.error(error.response.data.message);
|
||||||
|
}
|
||||||
|
actionBtnLoadingState.setFinish();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSignupBtnsClick = async () => {
|
||||||
|
if (actionBtnLoadingState.isLoading) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const emailValidResult = validate(email, validateConfig);
|
||||||
|
if (!emailValidResult.result) {
|
||||||
|
toastHelper.error("Email: " + emailValidResult.reason);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const passwordValidResult = validate(password, validateConfig);
|
||||||
|
if (!passwordValidResult.result) {
|
||||||
|
toastHelper.error("Password: " + passwordValidResult.reason);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
actionBtnLoadingState.setLoading();
|
||||||
|
await api.signup(email, password);
|
||||||
|
const user = await userService.doSignIn();
|
||||||
|
if (user) {
|
||||||
|
navigate("/", {
|
||||||
|
replace: true,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
toastHelper.error("Signup failed");
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
@ -83,33 +117,50 @@ const Auth: React.FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="page-wrapper auth">
|
<div className="flex flex-row justify-center items-center w-full h-screen bg-white">
|
||||||
<div className="page-container">
|
<div className="w-80 max-w-full h-full py-4 flex flex-col justify-start items-center">
|
||||||
<div className="auth-form-wrapper">
|
<div className="w-full py-4 grow flex flex-col justify-center items-center">
|
||||||
<div className="page-header-container">
|
<div className="flex flex-col justify-start items-start w-full mb-4">
|
||||||
<div className="title-container">
|
<div className="text-3xl font-medium font-mono flex flex-row justify-start items-center">
|
||||||
<img className="logo-img" src="/logo-full.webp" alt="" />
|
Corgi
|
||||||
</div>
|
<Only when={actionBtnLoadingState.isLoading}>
|
||||||
<p className="slogan-text">Corgi</p>
|
<Icon.Loader className="ml-2 w-5 h-auto animate-spin" />
|
||||||
</div>
|
</Only>
|
||||||
<div className={`page-content-container ${actionBtnLoadingState.isLoading ? "requesting" : ""}`}>
|
|
||||||
<div className="form-item-container input-form-container">
|
|
||||||
<span className={`normal-text ${email ? "not-null" : ""}`}>Email</span>
|
|
||||||
<input type="email" value={email} onChange={handleEmailInputChanged} />
|
|
||||||
</div>
|
|
||||||
<div className="form-item-container input-form-container">
|
|
||||||
<span className={`normal-text ${password ? "not-null" : ""}`}>Password</span>
|
|
||||||
<input type="password" value={password} onChange={handlePasswordInputChanged} />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="action-btns-container">
|
<div className={`flex flex-col justify-start items-start w-full ${actionBtnLoadingState.isLoading ? "opacity-80" : ""}`}>
|
||||||
|
<div className="w-full flex flex-col mb-2">
|
||||||
|
<span className="leading-8 mb-1 text-gray-600">Email</span>
|
||||||
|
<input
|
||||||
|
className="border rounded-md px-3 p-2 leading-7 focus:border-blue-600"
|
||||||
|
type="email"
|
||||||
|
value={email}
|
||||||
|
onChange={handleEmailInputChanged}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="w-full flex flex-col mb-2">
|
||||||
|
<span className="leading-8 text-gray-600">Password</span>
|
||||||
|
<input
|
||||||
|
className="border rounded-md px-3 p-2 leading-7 focus:border-blue-600"
|
||||||
|
type="password"
|
||||||
|
value={password}
|
||||||
|
onChange={handlePasswordInputChanged}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="w-full flex flex-row justify-end items-center mt-4">
|
||||||
<button
|
<button
|
||||||
className={`btn signin-btn ${actionBtnLoadingState.isLoading ? "requesting" : ""}`}
|
className={`mr-4 text-gray-600 hover:text-black ${actionBtnLoadingState.isLoading ? "opacity-80 cursor-wait" : ""}`}
|
||||||
|
onClick={() => handleSignupBtnsClick()}
|
||||||
|
>
|
||||||
|
Sign up
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className={`border rounded-md border-green-600 bg-green-600 text-white px-3 py-2 leading-6 hover:bg-green-700 ${
|
||||||
|
actionBtnLoadingState.isLoading ? "opacity-80 cursor-wait" : ""
|
||||||
|
}`}
|
||||||
onClick={() => handleSigninBtnsClick()}
|
onClick={() => handleSigninBtnsClick()}
|
||||||
>
|
>
|
||||||
<Only when={actionBtnLoadingState.isLoading}>
|
|
||||||
<Icon.Loader className="img-icon" />
|
|
||||||
</Only>
|
|
||||||
Sign in
|
Sign in
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -25,7 +25,7 @@ const Home: React.FC = () => {
|
|||||||
<p className="font-mono mb-2 text-gray-400">Workspace List</p>
|
<p className="font-mono mb-2 text-gray-400">Workspace List</p>
|
||||||
<WorkspaceListView workspaceList={workspaceList} />
|
<WorkspaceListView workspaceList={workspaceList} />
|
||||||
<div
|
<div
|
||||||
className="flex flex-row justify-start items-center border px-3 py-3 rounded-lg mt-4 cursor-pointer"
|
className="flex flex-row justify-start items-center border px-3 py-3 rounded-lg cursor-pointer"
|
||||||
onClick={() => showCreateWorkspaceDialog()}
|
onClick={() => showCreateWorkspaceDialog()}
|
||||||
>
|
>
|
||||||
<Icon.Plus className="w-5 h-auto mr-1" /> Create Workspace
|
<Icon.Plus className="w-5 h-auto mr-1" /> Create Workspace
|
||||||
|
@ -48,16 +48,19 @@ const WorkspaceDetail: React.FC = () => {
|
|||||||
<Header />
|
<Header />
|
||||||
{loadingState.isLoading ? null : (
|
{loadingState.isLoading ? null : (
|
||||||
<div className="mx-auto max-w-4xl w-full px-3 py-6 flex flex-col justify-start items-start">
|
<div className="mx-auto max-w-4xl w-full px-3 py-6 flex flex-col justify-start items-start">
|
||||||
<div
|
<div className="w-full flex flex-row justify-start items-center mb-4">
|
||||||
className="flex flex-row justify-start items-center mb-4 text-gray-600 border rounded px-2 py-1 cursor-pointer"
|
<div
|
||||||
onClick={() => handleBackToHome()}
|
className="flex flex-row justify-start items-center text-gray-600 border rounded-md px-2 py-1 cursor-pointer"
|
||||||
>
|
onClick={() => handleBackToHome()}
|
||||||
<Icon.ChevronLeft className="w-5 h-auto mr-1" /> Back to Home
|
>
|
||||||
|
<Icon.ChevronLeft className="w-5 h-auto" /> Back to Home
|
||||||
|
</div>
|
||||||
|
<span className="ml-4 font-mono text-gray-600">Workspace: {state?.workspace.name}</span>
|
||||||
</div>
|
</div>
|
||||||
<p className="font-mono mb-2 text-gray-600">Workspace: {state?.workspace.name}</p>
|
<p className="font-mono mb-2 text-gray-400">Shortcut List</p>
|
||||||
<ShortcutListView shortcutList={shortcutList} />
|
<ShortcutListView workspaceId={state.workspace.id} shortcutList={shortcutList} />
|
||||||
<div
|
<div
|
||||||
className="flex flex-row justify-start items-center border px-3 py-3 rounded-lg mt-4 cursor-pointer"
|
className="flex flex-row justify-start items-center border px-3 py-3 rounded-lg cursor-pointer"
|
||||||
onClick={() => showCreateShortcutDialog(state.workspace.id)}
|
onClick={() => showCreateShortcutDialog(state.workspace.id)}
|
||||||
>
|
>
|
||||||
<Icon.Plus className="w-5 h-auto mr-1" /> Create Shortcut
|
<Icon.Plus className="w-5 h-auto mr-1" /> Create Shortcut
|
||||||
|
Loading…
x
Reference in New Issue
Block a user