-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
reorganize, make projects listing and creation work
- Loading branch information
Showing
11 changed files
with
339 additions
and
56 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
21 changes: 21 additions & 0 deletions
21
src/features/projects-management/ProjectsManagementContext.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import { createContext, useContext } from "react"; | ||
import { ProjectListingItem } from "./projects-management-entities"; | ||
|
||
export interface ProjectsManagementContextValue { | ||
items: ProjectListingItem[]; | ||
create: (name: string) => Promise<void>; | ||
isLoading: boolean; | ||
isError: Error | null; | ||
} | ||
|
||
export const ProjectsManagementContext = | ||
createContext<ProjectsManagementContextValue | null>(null); | ||
|
||
export function useProjectsManagement(): ProjectsManagementContextValue { | ||
const contextValue = useContext(ProjectsManagementContext); | ||
if (contextValue === null) { | ||
throw new Error("Hook used outside its context."); | ||
} | ||
|
||
return contextValue; | ||
} |
50 changes: 50 additions & 0 deletions
50
src/features/projects-management/ProjectsManagementContextProvider.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
import { ReactNode, useCallback, useEffect, useState } from "react"; | ||
import { | ||
persistNewProjectListingItem, | ||
retrieveProjectsListing, | ||
} from "./opfs-projects-listing-service"; | ||
import { ProjectListingItem } from "./projects-management-entities"; | ||
import { ProjectsManagementContext } from "./ProjectsManagementContext"; | ||
|
||
export function ProjectsManagementContextProvider({ | ||
children, | ||
}: { | ||
children: ReactNode; | ||
}) { | ||
const [items, setProjects] = useState<ProjectListingItem[]>([]); | ||
const [isLoading, setIsLoading] = useState<boolean>(true); | ||
const [isError, setError] = useState<Error | null>(null); | ||
|
||
const pullProjectsListing = useCallback(async () => { | ||
setError(null); | ||
setIsLoading(true); | ||
retrieveProjectsListing() | ||
.then((projects) => setProjects(projects.items)) | ||
.catch((error) => setError(error)) | ||
.finally(() => setIsLoading(false)); | ||
}, []); | ||
|
||
useEffect(() => { | ||
pullProjectsListing(); | ||
}, [pullProjectsListing]); | ||
|
||
const create = useCallback( | ||
async (name: string) => { | ||
setError(null); | ||
setIsLoading(true); | ||
persistNewProjectListingItem(name) | ||
.catch((error) => setError(error)) | ||
.finally(() => setIsLoading(false)) | ||
.then(() => pullProjectsListing()); | ||
}, | ||
[pullProjectsListing] | ||
); | ||
|
||
return ( | ||
<ProjectsManagementContext.Provider | ||
value={{ items, create, isLoading, isError }} | ||
> | ||
{children} | ||
</ProjectsManagementContext.Provider> | ||
); | ||
} |
65 changes: 65 additions & 0 deletions
65
src/features/projects-management/ProjectsManagementPage.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
import { FormEvent, useCallback, useState } from "react"; | ||
import { useProjectsManagement } from "./ProjectsManagementContext"; | ||
import { ProjectsManagementContextProvider } from "./ProjectsManagementContextProvider"; | ||
|
||
export function ProjectsManagementPage() { | ||
return ( | ||
<ProjectsManagementContextProvider> | ||
<main> | ||
<ProjectForm /> | ||
<ProjectsList /> | ||
</main> | ||
</ProjectsManagementContextProvider> | ||
); | ||
} | ||
|
||
function ProjectsList() { | ||
const { items: projects, isLoading, isError } = useProjectsManagement(); | ||
|
||
if (isLoading) { | ||
return <div>Loading...</div>; | ||
} | ||
|
||
if (isError) { | ||
return <div>Error: {isError.message}</div>; | ||
} | ||
|
||
return ( | ||
<ul> | ||
{projects.map((project) => ( | ||
<li key={project.uuid}>{project.name}</li> | ||
))} | ||
</ul> | ||
); | ||
} | ||
|
||
function ProjectForm() { | ||
const { create } = useProjectsManagement(); | ||
|
||
const [name, setName] = useState(""); | ||
|
||
const handleSubmit = useCallback( | ||
(event: FormEvent<HTMLFormElement>) => { | ||
event.preventDefault(); | ||
create(name); | ||
}, | ||
[create, name] | ||
); | ||
|
||
return ( | ||
<form onSubmit={handleSubmit}> | ||
<label htmlFor="name">Project name</label> | ||
<input | ||
type="text" | ||
id="name" | ||
value={name} | ||
onChange={(event) => setName(event.target.value)} | ||
autoComplete="off" | ||
maxLength={50} | ||
spellCheck={false} | ||
required | ||
/> | ||
<button type="submit">Create</button> | ||
</form> | ||
); | ||
} |
62 changes: 62 additions & 0 deletions
62
src/features/projects-management/opfs-projects-listing-service.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
import { v4 as uuidv4 } from "uuid"; | ||
import { Project } from "../../entities/management"; | ||
import { | ||
makeOpfsFileAdapter, | ||
makeOpfsMainDirAdapter, | ||
} from "../../services/origin-private-file-system"; | ||
import { | ||
ProjectListing, | ||
ProjectListingItem, | ||
} from "./projects-management-entities"; | ||
|
||
export async function retrieveProjectsListing(): Promise<ProjectListing> { | ||
const opfsDir = await makeOpfsMainDirAdapter({ | ||
subdir: PROJECTS_OPFS_SUBDIRECTORY, | ||
}); | ||
const filenames = await opfsDir.retrieveFilenames(); | ||
return { | ||
items: filenames.map((filename) => { | ||
const rFilename = | ||
/(?<uuid>[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})_(?<name>[A-Za-z ]+)\.json/; | ||
const match = filename.match(rFilename); | ||
if (!match || !match.groups) { | ||
throw new Error( | ||
`Invalid project filename (corrupted data?): ${filename}` | ||
); | ||
} | ||
|
||
return { | ||
uuid: match.groups.uuid, | ||
name: match.groups.name, | ||
}; | ||
}), | ||
}; | ||
} | ||
|
||
export async function persistNewProjectListingItem( | ||
name: string | ||
): Promise<ProjectListingItem> { | ||
if (!name) { | ||
throw new Error("Project name cannot be empty."); | ||
} | ||
|
||
const item: ProjectListingItem = { | ||
uuid: uuidv4(), | ||
name: name.trim().replace(".json", ""), | ||
}; | ||
|
||
const file = await makeOpfsFileAdapter<Project>({ | ||
filename: `${item.uuid}_${item.name}.json`, | ||
subdir: PROJECTS_OPFS_SUBDIRECTORY, | ||
}); | ||
await file.persist({ | ||
uuid: item.uuid, | ||
name: item.name, | ||
sections: [], | ||
requests: [], | ||
}); | ||
|
||
return item; | ||
} | ||
|
||
export const PROJECTS_OPFS_SUBDIRECTORY = "projects"; |
8 changes: 8 additions & 0 deletions
8
src/features/projects-management/projects-management-entities.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
export type ProjectListing = { | ||
items: ProjectListingItem[]; | ||
}; | ||
|
||
export interface ProjectListingItem { | ||
uuid: string; | ||
name: string; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.