mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2025-11-16 09:41:31 +10:00
feat(resume): ✨ implement resume locking feature
This commit is contained in:
@ -1,6 +1,6 @@
|
||||
import { HouseSimple, SidebarSimple } from "@phosphor-icons/react";
|
||||
import { HouseSimple, Lock, SidebarSimple } from "@phosphor-icons/react";
|
||||
import { useBreakpoint } from "@reactive-resume/hooks";
|
||||
import { Button } from "@reactive-resume/ui";
|
||||
import { Button, Tooltip } from "@reactive-resume/ui";
|
||||
import { cn } from "@reactive-resume/utils";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
@ -11,8 +11,10 @@ export const BuilderHeader = () => {
|
||||
const { isDesktop } = useBreakpoint();
|
||||
const defaultPanelSize = isDesktop ? 25 : 0;
|
||||
|
||||
const toggle = useBuilderStore((state) => state.toggle);
|
||||
const title = useResumeStore((state) => state.resume.title);
|
||||
const locked = useResumeStore((state) => state.resume.locked);
|
||||
|
||||
const toggle = useBuilderStore((state) => state.toggle);
|
||||
const isDragging = useBuilderStore(
|
||||
(state) => state.panel.left.isDragging || state.panel.right.isDragging,
|
||||
);
|
||||
@ -48,6 +50,12 @@ export const BuilderHeader = () => {
|
||||
<span className="mr-2 text-xs opacity-40">{"/"}</span>
|
||||
|
||||
<h1 className="font-medium">{title}</h1>
|
||||
|
||||
{locked && (
|
||||
<Tooltip content="This resume is locked, please unlock to make further changes.">
|
||||
<Lock size={14} className="ml-2 opacity-75" />
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<Button size="icon" variant="ghost" onClick={() => onToggle("right")}>
|
||||
|
||||
@ -2,9 +2,9 @@ import {
|
||||
ArrowCounterClockwise,
|
||||
Broom,
|
||||
Columns,
|
||||
DotsThreeVertical,
|
||||
Eye,
|
||||
EyeSlash,
|
||||
List,
|
||||
PencilSimple,
|
||||
Plus,
|
||||
TrashSimple,
|
||||
@ -55,7 +55,7 @@ export const SectionOptions = ({ id }: Props) => {
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="ghost" size="icon">
|
||||
<DotsThreeVertical weight="bold" />
|
||||
<List weight="bold" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent className="w-48">
|
||||
|
||||
@ -7,6 +7,7 @@ import { ThemeSwitch } from "@/client/components/theme-switch";
|
||||
import { ExportSection } from "./sections/export";
|
||||
import { InformationSection } from "./sections/information";
|
||||
import { LayoutSection } from "./sections/layout";
|
||||
import { NotesSection } from "./sections/notes";
|
||||
import { PageSection } from "./sections/page";
|
||||
import { SharingSection } from "./sections/sharing";
|
||||
import { StatisticsSection } from "./sections/statistics";
|
||||
@ -43,6 +44,8 @@ export const RightSidebar = () => {
|
||||
<Separator />
|
||||
<ExportSection />
|
||||
<Separator />
|
||||
<NotesSection />
|
||||
<Separator />
|
||||
<InformationSection />
|
||||
<Separator />
|
||||
<Copyright className="text-center" />
|
||||
@ -63,6 +66,18 @@ export const RightSidebar = () => {
|
||||
<SectionIcon id="theme" name="Theme" onClick={() => scrollIntoView("#theme")} />
|
||||
<SectionIcon id="page" name="Page" onClick={() => scrollIntoView("#page")} />
|
||||
<SectionIcon id="sharing" name="Sharing" onClick={() => scrollIntoView("#sharing")} />
|
||||
<SectionIcon
|
||||
id="statistics"
|
||||
name="Statistics"
|
||||
onClick={() => scrollIntoView("#statistics")}
|
||||
/>
|
||||
<SectionIcon id="export" name="Export" onClick={() => scrollIntoView("#export")} />
|
||||
<SectionIcon id="notes" name="Notes" onClick={() => scrollIntoView("#notes")} />
|
||||
<SectionIcon
|
||||
id="information"
|
||||
name="Information"
|
||||
onClick={() => scrollIntoView("#information")}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<ThemeSwitch size={14} />
|
||||
|
||||
@ -0,0 +1,37 @@
|
||||
import { RichInput } from "@reactive-resume/ui";
|
||||
|
||||
import { useResumeStore } from "@/client/stores/resume";
|
||||
|
||||
import { getSectionIcon } from "../shared/section-icon";
|
||||
|
||||
export const NotesSection = () => {
|
||||
const setValue = useResumeStore((state) => state.setValue);
|
||||
const notes = useResumeStore((state) => state.resume.data.metadata.notes);
|
||||
|
||||
return (
|
||||
<section id="notes" className="grid gap-y-6">
|
||||
<header className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-x-4">
|
||||
{getSectionIcon("notes")}
|
||||
<h2 className="line-clamp-1 text-3xl font-bold">Notes</h2>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main className="grid gap-y-4">
|
||||
<p className="leading-relaxed">
|
||||
This section is reserved for your personal notes specific to this resume. The content here
|
||||
remains private and is not shared with anyone else.
|
||||
</p>
|
||||
|
||||
<div className="space-y-1.5">
|
||||
<RichInput content={notes} onChange={(content) => setValue("metadata.notes", content)} />
|
||||
|
||||
<p className="text-xs leading-relaxed opacity-75">
|
||||
For example, information regarding which companies you sent this resume to or the links
|
||||
to the job descriptions can be noted down here.
|
||||
</p>
|
||||
</div>
|
||||
</main>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
@ -4,6 +4,7 @@ import {
|
||||
IconProps,
|
||||
Info,
|
||||
Layout,
|
||||
Note,
|
||||
Palette,
|
||||
ReadCvLogo,
|
||||
ShareFat,
|
||||
@ -13,6 +14,7 @@ import {
|
||||
import { Button, ButtonProps, Tooltip } from "@reactive-resume/ui";
|
||||
|
||||
export type MetadataKey =
|
||||
| "notes"
|
||||
| "template"
|
||||
| "layout"
|
||||
| "typography"
|
||||
@ -26,6 +28,8 @@ export type MetadataKey =
|
||||
export const getSectionIcon = (id: MetadataKey, props: IconProps = {}) => {
|
||||
switch (id) {
|
||||
// Left Sidebar
|
||||
case "notes":
|
||||
return <Note size={18} {...props} />;
|
||||
case "template":
|
||||
return <DiamondsFour size={18} {...props} />;
|
||||
case "layout":
|
||||
|
||||
Reference in New Issue
Block a user