fix: icon picker not working (#1080)

* fix: icon picker not working

* fix: project icon in analytics sidebar
This commit is contained in:
Aaryan Khandelwal 2023-05-19 16:35:51 +05:30 committed by GitHub
parent e3a114cd69
commit 7f5fdb9589
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 158 additions and 71 deletions

View File

@ -153,12 +153,21 @@ export const AnalyticsSidebar: React.FC<Props> = ({
return ( return (
<div key={project.id}> <div key={project.id}>
<h5 className="text-sm flex items-center gap-1"> <h5 className="text-sm flex items-center gap-1">
{project.icon ? ( {project.emoji ? (
<span className="grid h-6 w-6 flex-shrink-0 place-items-center"> <span className="grid h-6 w-6 flex-shrink-0 place-items-center">
{String.fromCodePoint(parseInt(project.icon))} {String.fromCodePoint(parseInt(project.emoji))}
</span> </span>
) : project.icon_prop ? (
<div className="h-6 w-6 grid place-items-center flex-shrink-0">
<span
style={{ color: project.icon_prop.color }}
className="material-symbols-rounded text-lg"
>
{project.icon_prop.name}
</span>
</div>
) : ( ) : (
<span className="grid h-8 w-8 flex-shrink-0 place-items-center rounded bg-gray-700 uppercase text-white"> <span className="grid h-6 w-6 mr-1 flex-shrink-0 place-items-center rounded bg-gray-700 uppercase text-white">
{project?.name.charAt(0)} {project?.name.charAt(0)}
</span> </span>
)} )}
@ -254,12 +263,21 @@ export const AnalyticsSidebar: React.FC<Props> = ({
) : ( ) : (
<div className="hidden md:flex md:flex-col h-full overflow-y-auto"> <div className="hidden md:flex md:flex-col h-full overflow-y-auto">
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
{projectDetails?.icon ? ( {projectDetails?.emoji ? (
<span className="grid h-6 w-6 flex-shrink-0 place-items-center"> <div className="grid h-6 w-6 flex-shrink-0 place-items-center">
{String.fromCodePoint(parseInt(projectDetails.icon))} {String.fromCodePoint(parseInt(projectDetails.emoji))}
</div>
) : projectDetails?.icon_prop ? (
<div className="h-6 w-6 grid place-items-center flex-shrink-0">
<span
style={{ color: projectDetails.icon_prop.color }}
className="material-symbols-rounded text-lg"
>
{projectDetails.icon_prop.name}
</span> </span>
</div>
) : ( ) : (
<span className="grid h-8 w-8 flex-shrink-0 place-items-center rounded bg-gray-700 uppercase text-white"> <span className="grid h-6 w-6 mr-1 flex-shrink-0 place-items-center rounded bg-gray-700 uppercase text-white">
{projectDetails?.name.charAt(0)} {projectDetails?.name.charAt(0)}
</span> </span>
)} )}

View File

@ -25,13 +25,7 @@ const tabOptions = [
}, },
]; ];
const EmojiIconPicker: React.FC<Props> = ({ const EmojiIconPicker: React.FC<Props> = ({ label, value, onChange, onIconColorChange }) => {
label,
value,
onChange,
onIconColorChange,
onIconsClick,
}) => {
const ref = useRef<HTMLDivElement>(null); const ref = useRef<HTMLDivElement>(null);
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
@ -181,13 +175,13 @@ const EmojiIconPicker: React.FC<Props> = ({
</div> </div>
<hr className="mb-1 h-[1px] w-full border-brand-base" /> <hr className="mb-1 h-[1px] w-full border-brand-base" />
<div className="mt-1 ml-1 grid grid-cols-8 gap-x-2 gap-y-3"> <div className="mt-1 ml-1 grid grid-cols-8 gap-x-2 gap-y-3">
{icons.material_rounded.map((icon) => ( {icons.material_rounded.map((icon, index) => (
<button <button
key={`${icon.name}-${index}`}
type="button" type="button"
className="mb-1 flex h-4 w-4 select-none items-center text-lg" className="mb-1 flex h-4 w-4 select-none items-center text-lg"
key={icon.name}
onClick={() => { onClick={() => {
if (onIconsClick) onIconsClick(icon.name); onChange({ name: icon.name, color: activeColor });
setIsOpen(false); setIsOpen(false);
}} }}
> >

View File

@ -1,7 +1,13 @@
export type Props = { export type Props = {
label: string | React.ReactNode; label: string | React.ReactNode;
value: any; value: any;
onChange: (data: any) => void; onChange: (
onIconsClick?: (data: any) => void; data:
| string
| {
name: string;
color: string;
}
) => void;
onIconColorChange?: (data: any) => void; onIconColorChange?: (data: any) => void;
}; };

View File

@ -40,7 +40,7 @@ const defaultValues: Partial<IProject> = {
identifier: "", identifier: "",
description: "", description: "",
network: 2, network: 2,
icon: getRandomEmoji(), emoji_and_icon: getRandomEmoji(),
cover_image: cover_image:
"https://images.unsplash.com/photo-1575116464504-9e7652fddcb3?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwyODUyNTV8MHwxfHNlYXJjaHwxOHx8cGxhbmV8ZW58MHx8fHwxNjgxNDY4NTY5&ixlib=rb-4.0.3&q=80&w=1080", "https://images.unsplash.com/photo-1575116464504-9e7652fddcb3?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwyODUyNTV8MHwxfHNlYXJjaHwxOHx8cGxhbmV8ZW58MHx8fHwxNjgxNDY4NTY5&ixlib=rb-4.0.3&q=80&w=1080",
}; };
@ -113,8 +113,14 @@ export const CreateProjectModal: React.FC<Props> = (props) => {
const onSubmit = async (formData: IProject) => { const onSubmit = async (formData: IProject) => {
if (!workspaceSlug) return; if (!workspaceSlug) return;
const { emoji_and_icon, ...payload } = formData;
if (typeof formData.emoji_and_icon === "object") payload.icon_prop = formData.emoji_and_icon;
else payload.emoji = formData.emoji_and_icon;
await projectServices await projectServices
.createProject(workspaceSlug as string, formData) .createProject(workspaceSlug as string, payload)
.then((res) => { .then((res) => {
mutate<IProject[]>( mutate<IProject[]>(
PROJECTS_LIST(workspaceSlug as string), PROJECTS_LIST(workspaceSlug as string),
@ -164,7 +170,7 @@ export const CreateProjectModal: React.FC<Props> = (props) => {
leaveFrom="opacity-100" leaveFrom="opacity-100"
leaveTo="opacity-0" leaveTo="opacity-0"
> >
<div className="fixed inset-0 bg-[#131313] bg-opacity-50 transition-opacity" /> <div className="fixed inset-0 bg-brand-backdrop bg-opacity-50 transition-opacity" />
</Transition.Child> </Transition.Child>
<div className="fixed inset-0 z-20 overflow-y-auto"> <div className="fixed inset-0 z-20 overflow-y-auto">
@ -213,12 +219,31 @@ export const CreateProjectModal: React.FC<Props> = (props) => {
<div className="mt-5 space-y-4 px-4 py-3"> <div className="mt-5 space-y-4 px-4 py-3">
<div className="flex items-center gap-x-2"> <div className="flex items-center gap-x-2">
<div> <div>
<Controller
name="emoji_and_icon"
control={control}
render={({ field: { value, onChange } }) => (
<EmojiIconPicker <EmojiIconPicker
label={String.fromCodePoint(parseInt(watch("icon")))} label={
onChange={(emoji) => { value ? (
setValue("icon", emoji); typeof value === "object" ? (
}} <span
value={watch("icon")} style={{ color: value.color }}
className="material-symbols-rounded text-lg"
>
{value.name}
</span>
) : (
String.fromCodePoint(parseInt(value))
)
) : (
"Icon"
)
}
onChange={onChange}
value={value}
/>
)}
/> />
</div> </div>

View File

@ -185,11 +185,18 @@ export const SingleProjectCard: React.FC<ProjectCardProps> = ({
<a> <a>
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
<h3 className="text-1.5xl font-medium text-brand-base">{project.name}</h3> <h3 className="text-1.5xl font-medium text-brand-base">{project.name}</h3>
{project.icon && ( {project.emoji ? (
<span className="grid h-7 w-7 flex-shrink-0 place-items-center rounded uppercase"> <span className="grid h-7 w-7 flex-shrink-0 place-items-center rounded uppercase">
{String.fromCodePoint(parseInt(project.icon))} {String.fromCodePoint(parseInt(project.emoji))}
</span> </span>
)} ) : project.icon_prop ? (
<span
style={{ color: project.icon_prop.color }}
className="material-symbols-rounded text-lg"
>
{project.icon_prop.name}
</span>
) : null}
</div> </div>
<p className="mt-3.5 mb-7 break-all"> <p className="mt-3.5 mb-7 break-all">
{truncateText(project.description ?? "", 100)} {truncateText(project.description ?? "", 100)}

View File

@ -90,10 +90,19 @@ export const SingleSidebarProject: React.FC<Props> = ({
}`} }`}
> >
<div className="flex items-center gap-x-2"> <div className="flex items-center gap-x-2">
{project.icon ? ( {project.emoji ? (
<span className="grid h-7 w-7 flex-shrink-0 place-items-center rounded uppercase"> <span className="grid h-7 w-7 flex-shrink-0 place-items-center rounded uppercase">
{String.fromCodePoint(parseInt(project.icon))} {String.fromCodePoint(parseInt(project.emoji))}
</span> </span>
) : project.icon_prop ? (
<div className="h-7 w-7 grid place-items-center">
<span
style={{ color: project.icon_prop.color }}
className="material-symbols-rounded text-lg"
>
{project.icon_prop.name}
</span>
</div>
) : ( ) : (
<span className="grid h-7 w-7 flex-shrink-0 place-items-center rounded bg-gray-700 uppercase text-white"> <span className="grid h-7 w-7 flex-shrink-0 place-items-center rounded bg-gray-700 uppercase text-white">
{project?.name.charAt(0)} {project?.name.charAt(0)}

View File

@ -66,7 +66,6 @@ export const generateBarColor = (
type: "x_axis" | "segment" type: "x_axis" | "segment"
): string => { ): string => {
let color: string | undefined = generateRandomColor(value); let color: string | undefined = generateRandomColor(value);
console.log(value);
if (!analytics) return color; if (!analytics) return color;

View File

@ -56,27 +56,14 @@ const ControlSettings: NextPage = () => {
formState: { isSubmitting }, formState: { isSubmitting },
} = useForm<IProject>({ defaultValues }); } = useForm<IProject>({ defaultValues });
useEffect(() => {
if (projectDetails)
reset({
...projectDetails,
default_assignee: projectDetails.default_assignee?.id ?? projectDetails.default_assignee,
project_lead: projectDetails.project_lead?.id ?? projectDetails.project_lead,
workspace: (projectDetails.workspace as IWorkspace).id,
});
}, [projectDetails, reset]);
const onSubmit = async (formData: IProject) => { const onSubmit = async (formData: IProject) => {
if (!workspaceSlug || !projectId) return; if (!workspaceSlug || !projectId) return;
const payload: Partial<IProject> = { const payload: Partial<IProject> = {
name: formData.name,
network: formData.network,
identifier: formData.identifier,
description: formData.description,
default_assignee: formData.default_assignee, default_assignee: formData.default_assignee,
project_lead: formData.project_lead, project_lead: formData.project_lead,
icon: formData.icon,
}; };
await projectService await projectService
.updateProject(workspaceSlug as string, projectId as string, payload) .updateProject(workspaceSlug as string, projectId as string, payload)
.then((res) => { .then((res) => {
@ -94,6 +81,16 @@ const ControlSettings: NextPage = () => {
}); });
}; };
useEffect(() => {
if (projectDetails)
reset({
...projectDetails,
default_assignee: projectDetails.default_assignee?.id ?? projectDetails.default_assignee,
project_lead: projectDetails.project_lead?.id ?? projectDetails.project_lead,
workspace: (projectDetails.workspace as IWorkspace).id,
});
}, [projectDetails, reset]);
return ( return (
<ProjectAuthorizationWrapper <ProjectAuthorizationWrapper
breadcrumbs={ breadcrumbs={

View File

@ -74,8 +74,7 @@ const GeneralSettings: NextPage = () => {
if (projectDetails) if (projectDetails)
reset({ reset({
...projectDetails, ...projectDetails,
default_assignee: projectDetails.default_assignee?.id, emoji_and_icon: projectDetails.emoji ?? projectDetails.icon_prop,
project_lead: projectDetails.project_lead?.id,
workspace: (projectDetails.workspace as IWorkspace).id, workspace: (projectDetails.workspace as IWorkspace).id,
}); });
}, [projectDetails, reset]); }, [projectDetails, reset]);
@ -115,12 +114,17 @@ const GeneralSettings: NextPage = () => {
network: formData.network, network: formData.network,
identifier: formData.identifier, identifier: formData.identifier,
description: formData.description, description: formData.description,
default_assignee: formData.default_assignee,
project_lead: formData.project_lead,
icon: formData.icon,
cover_image: formData.cover_image, cover_image: formData.cover_image,
}; };
if (typeof formData.emoji_and_icon === "object") {
payload.emoji = null;
payload.icon_prop = formData.emoji_and_icon;
} else {
payload.emoji = formData.emoji_and_icon;
payload.icon_prop = null;
}
if (projectDetails.identifier !== formData.identifier) if (projectDetails.identifier !== formData.identifier)
await projectService await projectService
.checkProjectIdentifierAvailability(workspaceSlug as string, payload.identifier ?? "") .checkProjectIdentifierAvailability(workspaceSlug as string, payload.identifier ?? "")
@ -163,17 +167,34 @@ const GeneralSettings: NextPage = () => {
</div> </div>
<div className="col-span-12 flex gap-2 sm:col-span-6"> <div className="col-span-12 flex gap-2 sm:col-span-6">
{projectDetails ? ( {projectDetails ? (
<div className="h-7 w-7 grid place-items-center">
<Controller <Controller
control={control} control={control}
name="icon" name="emoji_and_icon"
render={({ field: { value, onChange } }) => ( render={({ field: { value, onChange } }) => (
<EmojiIconPicker <EmojiIconPicker
label={value ? String.fromCodePoint(parseInt(value)) : "Icon"} label={
value ? (
typeof value === "object" ? (
<span
style={{ color: value.color }}
className="material-symbols-rounded text-lg"
>
{value.name}
</span>
) : (
String.fromCodePoint(parseInt(value))
)
) : (
"Icon"
)
}
value={value} value={value}
onChange={onChange} onChange={onChange}
/> />
)} )}
/> />
</div>
) : ( ) : (
<Loader> <Loader>
<Loader.Item height="46px" width="46px" /> <Loader.Item height="46px" width="46px" />

View File

@ -9,22 +9,33 @@ import type {
} from "./"; } from "./";
export interface IProject { export interface IProject {
cover_image: string | null;
created_at: Date; created_at: Date;
created_by: string; created_by: string;
cover_image: string | null;
cycle_view: boolean; cycle_view: boolean;
issue_views_view: boolean;
module_view: boolean;
page_view: boolean;
default_assignee: IUser | string | null; default_assignee: IUser | string | null;
description: string; description: string;
emoji: string | null;
emoji_and_icon:
| string
| {
name: string;
color: string;
}
| null;
estimate: string | null; estimate: string | null;
icon: string; icon_prop: {
name: string;
color: string;
} | null;
id: string; id: string;
identifier: string; identifier: string;
is_favorite: boolean; is_favorite: boolean;
issue_views_view: boolean;
module_view: boolean;
name: string; name: string;
network: number; network: number;
page_view: boolean;
project_lead: IUser | string | null; project_lead: IUser | string | null;
slug: string; slug: string;
total_cycles: number; total_cycles: number;