-
Notifications
You must be signed in to change notification settings - Fork 2k
Feat/folder overview #6299
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Feat/folder overview #6299
Changes from 4 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| import styled from 'styled-components'; | ||
|
|
||
| const StyledWrapper = styled.div` | ||
| .editing-mode { | ||
| cursor: pointer; | ||
| color: ${(props) => props.theme.colors.text.yellow}; | ||
| } | ||
| `; | ||
|
|
||
| export default StyledWrapper; |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,121 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import 'github-markdown-css/github-markdown.css'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import get from 'lodash/get'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { updateFolderDocs } from 'providers/ReduxStore/slices/collections'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { useTheme } from 'providers/Theme'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { useState } from 'react'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { useDispatch, useSelector } from 'react-redux'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { saveFolderRoot } from 'providers/ReduxStore/slices/collections/actions'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import Markdown from 'components/MarkDown'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import CodeEditor from 'components/CodeEditor'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import StyledWrapper from './StyledWrapper'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { IconEdit, IconX, IconFileText } from '@tabler/icons'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const Docs = ({ collection, folder }) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const dispatch = useDispatch(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { displayedTheme } = useTheme(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const [isEditing, setIsEditing] = useState(false); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const rootDocs = get(folder, 'root.docs', ''); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const docs = folder.draft ? get(folder, 'draft.docs', rootDocs) : rootDocs; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const preferences = useSelector((state) => state.app.preferences); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const toggleViewMode = () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setIsEditing((prev) => !prev); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const onEdit = (value) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| dispatch(updateFolderDocs({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| folderUid: folder.uid, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| collectionUid: collection.uid, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| docs: value | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| })); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const handleDiscardChanges = () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| dispatch(updateFolderDocs({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| folderUid: folder.uid, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| collectionUid: collection.uid, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| docs: rootDocs | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| })); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| toggleViewMode(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const onSave = () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| dispatch(saveFolderRoot(collection.uid, folder.uid)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| toggleViewMode(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <StyledWrapper className="h-full w-full relative flex flex-col"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <div className="flex flex-row w-full justify-between items-center mb-4"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <div className="text-lg font-medium flex items-center gap-2"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <IconFileText size={20} strokeWidth={1.5} /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Documentation | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <div className="flex flex-row gap-2 items-center justify-center"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| {isEditing ? ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <div className="editing-mode" role="tab" onClick={handleDiscardChanges}> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <IconX className="cursor-pointer" size={20} strokeWidth={1.5} /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <button type="submit" className="submit btn btn-sm btn-secondary" onClick={onSave}> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Save | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </button> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) : ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <div className="editing-mode" role="tab" onClick={toggleViewMode}> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <IconEdit className="cursor-pointer" size={20} strokeWidth={1.5} /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+57
to
+67
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <div className="editing-mode" role="tab" onClick={handleDiscardChanges}> | |
| <IconX className="cursor-pointer" size={20} strokeWidth={1.5} /> | |
| </div> | |
| <button type="submit" className="submit btn btn-sm btn-secondary" onClick={onSave}> | |
| Save | |
| </button> | |
| </> | |
| ) : ( | |
| <div className="editing-mode" role="tab" onClick={toggleViewMode}> | |
| <IconEdit className="cursor-pointer" size={20} strokeWidth={1.5} /> | |
| </div> | |
| <button | |
| className="editing-mode" | |
| type="button" | |
| aria-label="Discard changes" | |
| onClick={handleDiscardChanges} | |
| > | |
| <IconX className="cursor-pointer" size={20} strokeWidth={1.5} /> | |
| </button> | |
| <button type="submit" className="submit btn btn-sm btn-secondary" onClick={onSave}> | |
| Save | |
| </button> | |
| </> | |
| ) : ( | |
| <button | |
| className="editing-mode" | |
| type="button" | |
| aria-label="Edit documentation" | |
| onClick={toggleViewMode} | |
| > | |
| <IconEdit className="cursor-pointer" size={20} strokeWidth={1.5} /> | |
| </button> |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,67 @@ | ||||||
| import React from 'react'; | ||||||
| import { getTotalRequestCountInCollection, getTreePathFromCollectionToItem, areItemsLoading, getItemsLoadStats } from 'utils/collections/'; | ||||||
| import { IconFolder, IconApi, IconSubtask } from '@tabler/icons'; | ||||||
|
|
||||||
| const Info = ({ collection, folder }) => { | ||||||
| const totalRequestsInFolder = getTotalRequestCountInCollection(folder); | ||||||
| const isFolderLoading = areItemsLoading(folder); | ||||||
| const { loading: itemsLoadingCount, total: totalItems } = getItemsLoadStats(folder); | ||||||
|
|
||||||
| // Count direct child folders | ||||||
| const childFoldersCount = (folder.items || []).filter((item) => item.type === 'folder').length; | ||||||
|
|
||||||
| // Get relative path from collection to folder | ||||||
| const treePath = getTreePathFromCollectionToItem(collection, folder); | ||||||
| const relativePath = treePath.map((item) => item.name).join(' / '); | ||||||
|
|
||||||
| return ( | ||||||
| <div className="w-full flex flex-col h-fit"> | ||||||
| <div className="rounded-lg py-6"> | ||||||
| <div className="grid gap-5"> | ||||||
| {/* Location Row */} | ||||||
| <div className="flex items-start"> | ||||||
| <div className="flex-shrink-0 p-3 bg-green-50 dark:bg-green-900/20 rounded-lg"> | ||||||
| <IconFolder className="w-5 h-5 text-green-500" stroke={1.5} /> | ||||||
| </div> | ||||||
| <div className="ml-4"> | ||||||
| <div className="font-medium">Location</div> | ||||||
| <div className="mt-1 text-muted break-all text-xs"> | ||||||
| {relativePath} | ||||||
| </div> | ||||||
| </div> | ||||||
| </div> | ||||||
|
|
||||||
| {/* Requests Row */} | ||||||
| <div className="flex items-start"> | ||||||
| <div className="flex-shrink-0 p-3 bg-purple-50 dark:bg-purple-900/20 rounded-lg"> | ||||||
| <IconApi className="w-5 h-5 text-purple-500" stroke={1.5} /> | ||||||
| </div> | ||||||
| <div className="ml-4"> | ||||||
| <div className="font-medium">Requests</div> | ||||||
| <div className="mt-1 text-muted text-xs"> | ||||||
| { | ||||||
| isFolderLoading ? `${totalItems - itemsLoadingCount} out of ${totalItems} requests in the folder loaded` : `${totalRequestsInFolder} request${totalRequestsInFolder !== 1 ? 's' : ''} in folder` | ||||||
|
||||||
| isFolderLoading ? `${totalItems - itemsLoadingCount} out of ${totalItems} requests in the folder loaded` : `${totalRequestsInFolder} request${totalRequestsInFolder !== 1 ? 's' : ''} in folder` | |
| isFolderLoading ? `${totalItems - itemsLoadingCount} out of ${totalItems} requests in the folder have been loaded` : `${totalRequestsInFolder} request${totalRequestsInFolder !== 1 ? 's' : ''} in folder` |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| import Docs from './Docs'; | ||
| import Info from './Info'; | ||
| import { IconFolder } from '@tabler/icons'; | ||
|
|
||
| const Overview = ({ collection, folder }) => { | ||
| return ( | ||
| <div className="h-full"> | ||
| <div className="grid grid-cols-5 gap-5 h-full"> | ||
| <div className="col-span-2"> | ||
| <div className="text-lg font-medium flex items-center gap-2"> | ||
| <IconFolder size={20} stroke={1.5} /> | ||
| {folder?.name} | ||
| </div> | ||
| <Info collection={collection} folder={folder} /> | ||
| </div> | ||
| <div className="col-span-3"> | ||
| <Docs collection={collection} folder={folder} /> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| ); | ||
| }; | ||
|
|
||
| export default Overview; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using
role="tab"on a div that functions as an edit button is semantically incorrect. This element is a button for toggling edit mode, not a tab. Consider removing the role attribute or changing it torole="button"with appropriatearia-labelfor better accessibility.