way nicer flow and almost ready for fetching and displaying but stay patient
This commit is contained in:
@@ -84,8 +84,15 @@ export default function Links() {
|
||||
</span>
|
||||
</Dropdown>
|
||||
|
||||
<Tooltip title={"code blocks: coming soon"}>
|
||||
<button className="bg-gray-300 bg-opacity-25 p-2 rounded hover:bg-opacity-40 ml-2">
|
||||
<Tooltip
|
||||
title={
|
||||
"code blocks: copy the link and paste it in here when you are done"
|
||||
}
|
||||
>
|
||||
<button
|
||||
onClick={() => window.open("https://www.codepile.net/", "_blank")}
|
||||
className="bg-gray-300 bg-opacity-25 p-2 rounded hover:bg-opacity-40 ml-2"
|
||||
>
|
||||
<FaCode className="text-lg text-white" />
|
||||
</button>
|
||||
</Tooltip>
|
||||
|
||||
@@ -193,7 +193,6 @@ export default function DashboardRoom() {
|
||||
{teamRooms.length}
|
||||
</span>
|
||||
</Radio.Button>
|
||||
|
||||
<Radio.Button value={RoomTypeFilter.live}>
|
||||
{RoomTypeFilter.live}{" "}
|
||||
<span className="text-xs text-orange-500">
|
||||
|
||||
@@ -2,13 +2,14 @@ import { FaArchive, FaExternalLinkAlt, FaFilePdf } from "react-icons/fa";
|
||||
import Link from "../models/link";
|
||||
import Image from "next/image";
|
||||
import { BsThreeDots } from "react-icons/bs";
|
||||
import LinkIcon from "./LinkIcon";
|
||||
|
||||
export default function LinkCard(props: { link?: Link }) {
|
||||
return (
|
||||
<span className="flex flex-col rounded-lg">
|
||||
{/* attmnt header */}
|
||||
<span className="flex flex-row bg-gray-300 bg-opacity-25 py-5 px-3 items-center justify-start">
|
||||
<RenderFileIcon className="text-4xl text-orange-300 mr-2" />
|
||||
<LinkIcon className="text-4xl text-orange-300 mr-2" />
|
||||
|
||||
<span className="flex flex-col items-baseline mr-10 space-y-1">
|
||||
<span className="text-md font-bold text-white">report.pdf</span>
|
||||
@@ -57,7 +58,3 @@ export default function LinkCard(props: { link?: Link }) {
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
function RenderFileIcon(props) {
|
||||
return <FaFilePdf {...props} />;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
import {
|
||||
FaAtlassian,
|
||||
FaFileAlt,
|
||||
FaFileCode,
|
||||
FaFileImage,
|
||||
FaFilePdf,
|
||||
FaGithubSquare,
|
||||
FaGoogleDrive,
|
||||
} from "react-icons/fa";
|
||||
import Link, { LinkType } from "../models/link";
|
||||
|
||||
export default function LinkIcon(props: {
|
||||
className?: string;
|
||||
linkType: LinkType;
|
||||
}) {
|
||||
// github
|
||||
|
||||
// atlassian
|
||||
|
||||
// drive
|
||||
|
||||
// onedrive
|
||||
|
||||
// dropbox
|
||||
|
||||
// image
|
||||
|
||||
// pdf
|
||||
|
||||
// https://www.codepile.net/ for code snippets
|
||||
|
||||
const { className, linkType, ...rest } = props;
|
||||
|
||||
switch (linkType) {
|
||||
case LinkType.default:
|
||||
return <FaFileAlt {...rest} className={`${className} text-slate-300 `} />;
|
||||
case LinkType.atlassian:
|
||||
return <FaAtlassian {...rest} className={`${className} text-sky-500 `} />;
|
||||
case LinkType.codePile:
|
||||
return <FaFileCode {...rest} className={`${className} text-blue-500 `} />;
|
||||
case LinkType.github:
|
||||
return (
|
||||
<FaGithubSquare {...rest} className={`${className} text-gray-500 `} />
|
||||
);
|
||||
case LinkType.googleDrive:
|
||||
return (
|
||||
<FaGoogleDrive {...rest} className={`${className} text-emerald-500 `} />
|
||||
);
|
||||
case LinkType.image:
|
||||
return (
|
||||
<FaFileImage {...rest} className={`${className} text-purple-500 `} />
|
||||
);
|
||||
case LinkType.pdf:
|
||||
<FaFilePdf {...rest} className={`${className} text-orange-500 `} />;
|
||||
default:
|
||||
return (
|
||||
<FaFileAlt {...rest} className={`${className} text-orange-500 `} />
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,40 +1,202 @@
|
||||
import { Divider, Select, Switch, Tooltip } from "antd";
|
||||
const { Option } = Select;
|
||||
|
||||
import Modal from "antd/lib/modal/Modal";
|
||||
import { useState } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
import { useAuth } from "../../contexts/authContext";
|
||||
import {
|
||||
ShowModalType,
|
||||
useKeyboardContext,
|
||||
} from "../../contexts/keyboardContext";
|
||||
import { useTeamDashboardContext } from "../../contexts/teamDashboardContext";
|
||||
import Link, { LinkType } from "../../models/link";
|
||||
import LinkIcon from "../LinkIcon";
|
||||
|
||||
export default function CreateOrUpdateLink() {
|
||||
const { currUser } = useAuth();
|
||||
const { handleModalType, showModalType } = useKeyboardContext();
|
||||
const { teamUsers, team } = useTeamDashboardContext();
|
||||
|
||||
function closeModal() {
|
||||
handleModalType(ShowModalType.na);
|
||||
}
|
||||
|
||||
function handleSelectRecipients(value) {
|
||||
// passed in array of selections...userIds
|
||||
|
||||
setRecipientsSelected(value);
|
||||
|
||||
console.log(value);
|
||||
}
|
||||
|
||||
function handleSelectRecipientMode(isChecked) {
|
||||
// clear the list of recipients if team selected
|
||||
if (isChecked) {
|
||||
setRecipientsSelected([]);
|
||||
setIsTeamLink(true);
|
||||
} else {
|
||||
setRecipientsSelected([]);
|
||||
setIsTeamLink(false);
|
||||
}
|
||||
}
|
||||
|
||||
function handleSubmitLink() {
|
||||
if (!link || !name) {
|
||||
toast.error("Please add a valid link and name");
|
||||
return;
|
||||
}
|
||||
|
||||
// if it's a team link, make sure recipients is null ... but the model should handle this for us?
|
||||
|
||||
try {
|
||||
const newLink = new Link(
|
||||
name,
|
||||
description,
|
||||
link,
|
||||
team.id,
|
||||
recipientsSelected,
|
||||
currUser.uid
|
||||
);
|
||||
|
||||
console.log(newLink);
|
||||
|
||||
// send to database
|
||||
} catch (error) {
|
||||
toast.error("Something went wrong in sending the link.");
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
const [link, setLink] = useState<string>("");
|
||||
const [name, setName] = useState<string>("");
|
||||
const [description, setDescription] = useState<string>("");
|
||||
const [showMoreDetails, setShowMoreDetails] = useState<boolean>(false);
|
||||
const [isTeamLink, setIsTeamLink] = useState<boolean>(true);
|
||||
const [recipientsSelected, setRecipientsSelected] = useState<string[]>([]);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title="Link Details"
|
||||
visible={showModalType == ShowModalType.createLink}
|
||||
onCancel={closeModal}
|
||||
onOk={closeModal}
|
||||
onOk={handleSubmitLink}
|
||||
>
|
||||
<div className="flex flex-col">
|
||||
<div className="flex flex-col space-y-5">
|
||||
<span className="flex flex-col items-start">
|
||||
<span className="text-lg">Link</span>
|
||||
<span className="text-gray-300 text-xs mb-2">
|
||||
Please make sure this is valid for others to access.
|
||||
</span>
|
||||
{/* icon and link input */}
|
||||
<span className="flex flex-row items-center space-x-2 w-full">
|
||||
<LinkIcon className="text-3xl" linkType={Link.getLinkType(link)} />
|
||||
|
||||
<input
|
||||
autoFocus
|
||||
className="flex-1 rounded-lg bg-gray-50 p-3"
|
||||
value={link}
|
||||
placeholder="https://jira.atlassian.com/team/xxx"
|
||||
onChange={(e) => setLink(e.target.value)}
|
||||
/>
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<span className="flex flex-col items-start">
|
||||
<span className="text-lg">Name</span>
|
||||
<span className="text-gray-300 text-xs mb-2">
|
||||
Make sure this makes sense to who you are sending it to.
|
||||
</span>
|
||||
|
||||
<input
|
||||
className="w-full rounded-lg bg-gray-50 p-3"
|
||||
value={link}
|
||||
placeholder="https://drive.google.com/xxx"
|
||||
onChange={(e) => setLink(e.target.value)}
|
||||
value={name}
|
||||
placeholder="ex. User Story - Customer Creating Orders"
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
/>
|
||||
</span>
|
||||
|
||||
<span className="flex flex-col items-start">
|
||||
<span className="text-lg">Recipients</span>
|
||||
<span className="text-gray-300 text-xs mb-2">
|
||||
Choose team or individual recipients
|
||||
</span>
|
||||
|
||||
<span className="mb-2">
|
||||
<Switch
|
||||
defaultChecked
|
||||
onChange={handleSelectRecipientMode}
|
||||
checkedChildren={<span>Team</span>}
|
||||
unCheckedChildren={<span>Members</span>}
|
||||
/>
|
||||
</span>
|
||||
|
||||
{/* select team members to send to */}
|
||||
{!isTeamLink ? (
|
||||
<Select
|
||||
mode="multiple"
|
||||
allowClear
|
||||
style={{ width: "100%" }}
|
||||
placeholder="Please select team members"
|
||||
onChange={handleSelectRecipients}
|
||||
value={recipientsSelected}
|
||||
optionLabelProp="label"
|
||||
filterOption={(input, option) =>
|
||||
option.props.label.toLowerCase().indexOf(input.toLowerCase()) >=
|
||||
0
|
||||
}
|
||||
>
|
||||
{teamUsers.map((tu) => {
|
||||
return (
|
||||
<Option key={tu.id} label={tu.firstName}>
|
||||
{tu.firstName} {tu.lastName}
|
||||
</Option>
|
||||
);
|
||||
})}
|
||||
</Select>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</span>
|
||||
|
||||
<Tooltip
|
||||
title={"Private mode coming soon, but keep it collaborative for now."}
|
||||
className="flex flex-col"
|
||||
></Tooltip>
|
||||
|
||||
{showMoreDetails ? (
|
||||
<>
|
||||
<Divider />
|
||||
|
||||
<button
|
||||
className="text-sm text-gray-300 text-left underline decoration-gray-300"
|
||||
onClick={() => setShowMoreDetails(false)}
|
||||
>
|
||||
Hide details...
|
||||
</button>
|
||||
|
||||
<span className="flex flex-col items-start">
|
||||
<span className="text-md">Description</span>
|
||||
<span className="text-gray-300 text-xs mb-2">
|
||||
Optional - Add any pointers about this file.
|
||||
</span>
|
||||
|
||||
<textarea
|
||||
className="w-full rounded-lg bg-gray-50 p-3"
|
||||
value={description}
|
||||
placeholder="ex. Please review this and add feedback asap"
|
||||
onChange={(e) => setDescription(e.target.value)}
|
||||
/>
|
||||
</span>
|
||||
</>
|
||||
) : (
|
||||
<button
|
||||
className="text-sm text-gray-300 text-left underline decoration-gray-300"
|
||||
onClick={() => setShowMoreDetails(true)}
|
||||
>
|
||||
Click to show more details...
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
|
||||
@@ -190,6 +190,7 @@ export default function CreateOrUpdateRoomModal(props: IModalProps) {
|
||||
)}
|
||||
|
||||
<input
|
||||
autoFocus
|
||||
className="w-full rounded-lg bg-gray-50 p-3"
|
||||
value={roomLink}
|
||||
placeholder="https://meet.google.com/xxx-xxxx"
|
||||
|
||||
+65
-8
@@ -6,25 +6,82 @@ export default class Link {
|
||||
description: string;
|
||||
link: string; //url for file
|
||||
|
||||
state: AttachmentState = AttachmentState.active;
|
||||
|
||||
isLoomScreenshare: boolean = false;
|
||||
state: LinkState = LinkState.active;
|
||||
type: LinkType;
|
||||
|
||||
teamId: string;
|
||||
// if it's not a teamAttachment, then have a list of members who it's for
|
||||
private _recipients: string[]; // userIds
|
||||
|
||||
createdByUserId: string;
|
||||
createdDate: Timestamp;
|
||||
|
||||
constructor(id: string, name: string, description: string) {}
|
||||
constructor(
|
||||
_name: string,
|
||||
_description: string,
|
||||
_link: string,
|
||||
_teamId: string,
|
||||
recipientsArr: string[],
|
||||
_createdByUserId: string
|
||||
) {
|
||||
this.name = _name;
|
||||
this.description = _description;
|
||||
this.link = _link;
|
||||
this.teamId = _teamId;
|
||||
this.recipients = recipientsArr;
|
||||
this.createdByUserId = _createdByUserId;
|
||||
|
||||
this.type = Link.getLinkType(_link);
|
||||
}
|
||||
|
||||
public get recipients(): string[] {
|
||||
return this._recipients;
|
||||
}
|
||||
|
||||
public set recipients(arrUserIds: string[]) {
|
||||
if (!this._recipients || this._recipients?.length == 0) {
|
||||
this._recipients = null;
|
||||
} else {
|
||||
this._recipients = arrUserIds;
|
||||
}
|
||||
}
|
||||
|
||||
static getLinkType(url: string): LinkType {
|
||||
if (url.includes(LinkType.github)) {
|
||||
return LinkType.github;
|
||||
} else if (url.includes(LinkType.atlassian)) {
|
||||
return LinkType.atlassian;
|
||||
} else if (url.includes(LinkType.googleDrive)) {
|
||||
return LinkType.googleDrive;
|
||||
} else if (
|
||||
url.includes(".png") ||
|
||||
url.includes(".jpg") ||
|
||||
url.includes(".svg") ||
|
||||
url.includes(".gif")
|
||||
) {
|
||||
return LinkType.image;
|
||||
} else if (url.includes(LinkType.pdf)) {
|
||||
return LinkType.pdf;
|
||||
} else if (url.includes(LinkType.codePile)) {
|
||||
return LinkType.codePile;
|
||||
} else {
|
||||
return LinkType.default;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export enum AttachmentState {
|
||||
export enum LinkState {
|
||||
active = "active",
|
||||
archived = "archived",
|
||||
}
|
||||
|
||||
export enum AttachmentType {
|
||||
link = "link",
|
||||
screenshare = "",
|
||||
export enum LinkType {
|
||||
default = "default",
|
||||
github = "github",
|
||||
atlassian = "atlassian",
|
||||
googleDrive = "drive.google",
|
||||
onedrive = "onedrive",
|
||||
image = "image",
|
||||
pdf = "pdf",
|
||||
codePile = "codepile",
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"folders": [
|
||||
{
|
||||
"path": "."
|
||||
},
|
||||
{
|
||||
"path": "..\\nirvana-web"
|
||||
}
|
||||
],
|
||||
"settings": {}
|
||||
}
|
||||
Reference in New Issue
Block a user