From 77ee9733559541f36d463bdc20104b07a3aa104a Mon Sep 17 00:00:00 2001 From: joostdecock Date: Sat, 1 Feb 2025 17:09:06 +0100 Subject: [PATCH] wip: Saving works now --- config/exceptions.yaml | 1 + .../Editor/components/HeaderMenu.mjs | 71 +++--- .../Editor/components/LoadingStatus.mjs | 18 +- .../components/views/MeasurementsView.mjs | 11 +- .../Editor/components/views/SaveView.mjs | 181 +++++++++++++++ .../Editor/components/views/index.mjs | 2 + .../react/components/Editor/config/index.mjs | 5 - .../react/components/Editor/lib/editor.mjs | 8 +- .../Editor/swizzle/components/save-view.mjs | 214 ------------------ packages/react/components/Editor/utils.mjs | 4 - packages/react/components/Heading/index.mjs | 37 +++ packages/react/components/Input/index.mjs | 14 +- packages/react/components/Link/index.mjs | 22 ++ packages/react/package.json | 1 + packages/utils/src/index.mjs | 15 +- packages/utils/src/loading-messages.mjs | 27 +++ sites/org/src/pages/signin/index.js | 4 +- sites/org/themes/dark.mjs | 2 +- sites/org/themes/light.mjs | 4 +- 19 files changed, 363 insertions(+), 278 deletions(-) create mode 100644 packages/react/components/Editor/components/views/SaveView.mjs delete mode 100644 packages/react/components/Editor/swizzle/components/save-view.mjs delete mode 100644 packages/react/components/Editor/utils.mjs create mode 100644 packages/react/components/Heading/index.mjs create mode 100644 packages/utils/src/loading-messages.mjs diff --git a/config/exceptions.yaml b/config/exceptions.yaml index 3dfd00412bd..74d3d57d23a 100644 --- a/config/exceptions.yaml +++ b/config/exceptions.yaml @@ -83,6 +83,7 @@ packageJson: "./components/Design": "./components/Design/index.mjs" "./components/Docusaurus": "./components/Docusaurus/index.mjs" "./components/Editor": "./components/Editor/index.mjs" + "./components/Heading": "./components/Heading/index.mjs" "./components/Highlight": "./components/Highlight/index.mjs" "./components/Icon": "./components/Icon/index.mjs" "./components/Input": "./components/Input/index.mjs" diff --git a/packages/react/components/Editor/components/HeaderMenu.mjs b/packages/react/components/Editor/components/HeaderMenu.mjs index d621102122e..5d7e7185ff7 100644 --- a/packages/react/components/Editor/components/HeaderMenu.mjs +++ b/packages/react/components/Editor/components/HeaderMenu.mjs @@ -2,6 +2,7 @@ import { missingMeasurements, flattenFlags } from '../lib/index.mjs' // Hooks import React, { useState } from 'react' +import { useBackend } from '@freesewing/react/hooks/useBackend' import { useDesignTranslation } from '@freesewing/react/hooks/useDesignTranslation' // Components import { Null } from './Null.mjs' @@ -207,11 +208,11 @@ export const HeaderMenuDraftViewFlags = (props) => { } export const HeaderMenuDraftViewIcons = (props) => { - const { update } = props + const { update, state } = props const Button = HeaderMenuButton const size = 'tw-w-5 tw-h-5' const muted = 'tw-text-current tw-opacity-50' - const ux = props.state.ui.ux + const ux = state.ui.ux const levels = { ...props.config.uxLevels.core, ...props.config.uxLevels.ui, @@ -225,25 +226,20 @@ export const HeaderMenuDraftViewIcons = (props) => { updateHandler={update.toggleSa} tooltip="Turns Seam Allowance on or off (see Core Settings)" > - + ) : null} {ux >= levels.units ? ( @@ -251,33 +247,33 @@ export const HeaderMenuDraftViewIcons = (props) => { {ux >= levels.paperless ? ( ) : null} {ux >= levels.complete ? ( ) : null} {ux >= levels.expand ? ( ) : null} @@ -285,24 +281,22 @@ export const HeaderMenuDraftViewIcons = (props) => { {ux >= levels.rotate ? ( ) : null} {ux >= levels.renderer ? ( ) : null} @@ -314,7 +308,7 @@ export const HeaderMenuUndoIcons = (props) => { const { update, state, Design } = props const Button = HeaderMenuButton const size = 'tw-w-5 tw-h-5' - const undos = props.state._?.undos && props.state._.undos.length > 0 ? props.state._.undos : false + const undos = state._?.undos && state._.undos.length > 0 ? state._.undos : false return (
@@ -386,18 +380,35 @@ export const HeaderMenuUndoIcons = (props) => { } export const HeaderMenuSaveIcons = (props) => { - const { update } = props + const { update, state } = props + const backend = useBackend() const Button = HeaderMenuButton const size = 'tw-w-5 tw-h-5' - const saveable = props.state._?.undos && props.state._.undos.length > 0 + const saveable = state._?.undos && state._.undos.length > 0 + + /* + * Save or Save As, depending on state.pattern + */ + const savePattern = async () => { + const pid = state.pid || false + if (pid) { + const loadingId = 'savePattern' + update.startLoading(loadingId) + const patternData = { + settings: state.settings, + } + const result = await backend.updatePattern(pid, patternData) + if (result[0] === 200) { + update.stopLoading(loadingId) + update.view('draft') + update.notifySuccess('Pattern saved') + } else update.notifyFailure('oops', loadingId) + } else update.view('save') + } return (
- + + ) : null} +

Save As New Pattern

+
+ + {withNotes ? ( +
+ + +
+ ) : ( + + )} + + } + /> + {withNotes ? ( + + ) : null} +
+ + +
+

+ To access your saved patterns, go to: + + {' '} + /account/patterns + +

+
+
+ + ) +} diff --git a/packages/react/components/Editor/components/views/index.mjs b/packages/react/components/Editor/components/views/index.mjs index 2084a6377ca..2e7f801ea87 100644 --- a/packages/react/components/Editor/components/views/index.mjs +++ b/packages/react/components/Editor/components/views/index.mjs @@ -3,6 +3,7 @@ import { ViewPicker } from './ViewPicker.mjs' import { DesignsView } from './DesignsView.mjs' import { MeasurementsView } from './MeasurementsView.mjs' import { DraftView } from './DraftView.mjs' +import { SaveView } from './SaveView.mjs' import { ErrorIcon } from '@freesewing/react/components/Icon' import { OptionsIcon, @@ -51,6 +52,7 @@ export const View = (props) => { if (view === 'designs') return if (view === 'measurements') return if (view === 'draft') return + if (view === 'save') return /* viewComponents: { draft: 'DraftView', diff --git a/packages/react/components/Editor/config/index.mjs b/packages/react/components/Editor/config/index.mjs index 4d931b961b3..04cf9d970f7 100644 --- a/packages/react/components/Editor/config/index.mjs +++ b/packages/react/components/Editor/config/index.mjs @@ -122,11 +122,6 @@ export const defaultConfig = { ux: 4, }, }, - classes: { - horFlex: 'flex flex-row items-center justify-between gap-4 w-full', - horFlexNoSm: 'md:flex md:flex-row md:items-center md:justify-between md:gap-4 md-w-full', - link: 'underline decoration-2 hover:decoration-4 text-secondary hover:text-secondary-focus', - }, roles: { levels: { readNone: 0, diff --git a/packages/react/components/Editor/lib/editor.mjs b/packages/react/components/Editor/lib/editor.mjs index 77cdceac62f..d1df7358eed 100644 --- a/packages/react/components/Editor/lib/editor.mjs +++ b/packages/react/components/Editor/lib/editor.mjs @@ -1,7 +1,7 @@ // Dependencies import React from 'react' import { defaultConfig } from '../config/index.mjs' -import { round, formatMm } from '@freesewing/utils' +import { round, formatMm, randomLoadingMessage, horFlexClasses } from '@freesewing/utils' import { formatDesignOptionValue, menuCoreSettingsStructure } from './index.mjs' import { menuUiPreferencesStructure } from './ui-preferences.mjs' // Components @@ -13,6 +13,7 @@ import { UiIcon, } from '@freesewing/react/components/Icon' import { HtmlSpan } from '../components/HtmlSpan.mjs' +import { Spinner } from '@freesewing/react/components/Spinner' /* * This method drafts the pattern @@ -531,6 +532,8 @@ export function stateUpdateFactory(setState, setEphemeralState, config) { return eph }) }, + // Pattern ID (pid) + pid: (val) => setState((cur) => ({ ...cur, pid: val })), ux: (val) => setState((cur) => objUpdate({ ...cur }, 'ux', val)), clearPattern: () => setState((cur) => { @@ -555,7 +558,8 @@ export function stateUpdateFactory(setState, setEphemeralState, config) { if (typeof newState.loading !== 'object') newState.loading = {} if (typeof conf.color === 'undefined') conf.color = 'info' newState.loading[id] = { - msg: t('pe:genericLoadingMsg'), + msg: randomLoadingMessage(), + icon: 'spinner', ...conf, } return newState diff --git a/packages/react/components/Editor/swizzle/components/save-view.mjs b/packages/react/components/Editor/swizzle/components/save-view.mjs deleted file mode 100644 index 4c1abb99b67..00000000000 --- a/packages/react/components/Editor/swizzle/components/save-view.mjs +++ /dev/null @@ -1,214 +0,0 @@ -import { useState } from 'react' -import yaml from 'js-yaml' - -export const SaveView = ({ Swizzled, state, update }) => { - // Hooks - const backend = Swizzled.hooks.useBackend() - const { t } = Swizzled.methods - - // State - const [name, setName] = useState( - `${Swizzled.methods.capitalize(state.design)} / ${Swizzled.methods.shortDate(status.locale)}` - ) - const [withNotes, setWithNotes] = useState(false) - const [notes, setNotes] = useState('') - const [savedId, setSavedId] = useState() - const [bookmarkedId] = useState() // FIXME - const [saveAs] = useState(false) // FIXME - - const addSettingsToNotes = () => { - setNotes( - notes + - '\n\n#### ' + - Swizzled.methods.t('pe:settings') + - '\n\n' + - '```yaml\n' + - yaml.dump(state.settings) + - '````' - ) - } - - const saveAsNewPattern = async () => { - const loadingId = 'savePatternAs' - update.startLoading(loadingId) - const patternData = { - design: state.design, - name, - public: false, - settings: state.settings, - data: {}, - } - if (withNotes) patternData.notes = notes - const result = await backend.createPattern(patternData) - if (result.success) { - const id = result.data.pattern.id - update.stopLoading(loadingId) - update.view('draft') - update.notifySuccess( - - {t('pe:patternSavedAs')}:{' '} - - /account/pattern?id={id} - - , - id - ) - } else update.notifyFailure('oops', loadingId) - } - - const savePattern = async () => { - //setLoadingStatus([true, 'savingPattern']) - const patternData = { - design: state.design, - settings: state.settings, - name, - public: false, - data: {}, - } - const result = await backend.updatePattern(saveAs.pattern, patternData) - if (result.success) { - //setLoadingStatus([ - // true, - // <> - // {t('status:patternSaved')} [#{saveAs.pattern}] - // , - // true, - // true, - //]) - setSavedId(saveAs.pattern) - update.notify({ color: 'success', msg: 'boom' }, saveAs.pattern) - } //else setLoadingStatus([true, 'backendError', true, false]) - } - - //const bookmarkPattern = async () => { - // setLoadingStatus([true, 'creatingBookmark']) - // const result = await backend.createBookmark({ - // type: 'pattern', - // title: name, - // url: window.location.pathname + window.location.search + window.location.hash, - // }) - // if (result.success) { - // const id = result.data.bookmark.id - // setLoadingStatus([ - // true, - // <> - // {t('status:bookmarkCreated')} [#{id}] - // , - // true, - // true, - // ]) - // setBookmarkedId(id) - // } else setLoadingStatus([true, 'backendError', true, false]) - //} - - return ( - - -
- {saveAs && saveAs.pattern ? ( - <> -

{t('pe:savePattern')}

- {savedId && ( - -
{t('workbend:patternSaved')}
- {t('pe:see')}:{' '} - -
- )} - - - ) : null} -

{t('pe:saveAsNewPattern')}

- {bookmarkedId && ( - -
{t('pe:patternBookmarkCreated')}
- {t('pe:see')}:{' '} - -
- )} -
- - {withNotes ? ( -
- - -
- ) : ( - - )} - - } - /> - {withNotes ? ( - - ) : null} -
- - -
-

- To access your saved patterns, go to:{' '} - - - /account/patterns - - -

-
-
-
- ) -} diff --git a/packages/react/components/Editor/utils.mjs b/packages/react/components/Editor/utils.mjs deleted file mode 100644 index 432989daaa1..00000000000 --- a/packages/react/components/Editor/utils.mjs +++ /dev/null @@ -1,4 +0,0 @@ -/* - * To spread icon + text horizontal - */ -export const horFlexClasses = 'flex flex-row items-center justify-between gap-4 w-full' diff --git a/packages/react/components/Heading/index.mjs b/packages/react/components/Heading/index.mjs new file mode 100644 index 00000000000..8619c7df99a --- /dev/null +++ b/packages/react/components/Heading/index.mjs @@ -0,0 +1,37 @@ +import React from 'react' + +export const H1 = ({ children }) => ( +

+ {children} +

+) + +export const H2 = ({ children }) => ( +

+ {children} +

+) + +export const H3 = ({ children }) => ( +

+ {children} +

+) + +export const H4 = ({ children }) => ( +

+ {children} +

+) + +export const H5 = ({ children }) => ( +
+ {children} +
+) + +export const H6 = ({ children }) => ( +
+ {children} +
+) diff --git a/packages/react/components/Input/index.mjs b/packages/react/components/Input/index.mjs index 62d72d0088c..c935a788672 100644 --- a/packages/react/components/Input/index.mjs +++ b/packages/react/components/Input/index.mjs @@ -138,7 +138,11 @@ export const NumberInput = ({ value={current} onChange={(evt) => update(evt.target.value)} className={`tw-daisy-input tw-w-full tw-daisy-input-bordered ${ - current === original ? 'input-secondary' : valid(current) ? 'input-success' : 'input-error' + current === original + ? 'tw-daisy-input-secondary' + : valid(current) + ? 'tw-daisy-input-success' + : 'tw-daisy-input-error' }`} {...{ max, min, step }} /> @@ -166,12 +170,12 @@ export const StringInput = ({ placeholder={placeholder} value={current} onChange={(evt) => update(evt.target.value)} - className={`tw-daisy-input tw-w-full tw-daisy-input-bordered ${ + className={`tw-daisy-input tw-w-full tw-daisy-input-bordered tw-text-current ${ current === original - ? 'daisy-input-secondary' + ? 'tw-daisy-input-secondary' : valid(current) - ? 'daisy-input-success' - : 'daisy-input-error' + ? 'tw-daisy-input-success' + : 'tw-daisy-input-error' }`} /> diff --git a/packages/react/components/Link/index.mjs b/packages/react/components/Link/index.mjs index e8525c53aa0..26f67bc40c2 100644 --- a/packages/react/components/Link/index.mjs +++ b/packages/react/components/Link/index.mjs @@ -33,6 +33,28 @@ export const Link = ({ href, title = false, children, className = linkClasses, s const BaseLink = Link +/** + * A regular link, but on a success background + * + * @param {object} props - All React props + * @param {array} props.children - The content to go in the layout + * @param {array} props.href - The target to link to + * @param {array} props.title - An optional link title + * @param {string} props.className - Any non-default CSS classes to apply + * @param {string} props.style - Any non-default styles to apply + */ +export const SuccessLink = ({ + href, + title = false, + children, + className = `${linkClasses} tw-text-success-content hover:tw-text-success-content`, + style = {}, +}) => ( + + {children} + +) + export const CardLink = ({ href, title, Icon, children, Link }) => { if (!Link) Link = BaseLink diff --git a/packages/react/package.json b/packages/react/package.json index fd443c9a7cd..ece5ad49b58 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -36,6 +36,7 @@ "./components/Design": "./components/Design/index.mjs", "./components/Docusaurus": "./components/Docusaurus/index.mjs", "./components/Editor": "./components/Editor/index.mjs", + "./components/Heading": "./components/Heading/index.mjs", "./components/Highlight": "./components/Highlight/index.mjs", "./components/Icon": "./components/Icon/index.mjs", "./components/Input": "./components/Input/index.mjs", diff --git a/packages/utils/src/index.mjs b/packages/utils/src/index.mjs index 40110257c52..54ed7c13579 100644 --- a/packages/utils/src/index.mjs +++ b/packages/utils/src/index.mjs @@ -3,6 +3,7 @@ import { cloudflare as cloudflareConfig } from '@freesewing/config' import _set from 'lodash/set.js' import _unset from 'lodash/unset.js' import _orderBy from 'lodash/orderBy.js' +import { loadingMessages } from './loading-messages.mjs' /* * Re-export lodash utils @@ -18,13 +19,14 @@ export const orderBy = _orderBy /* * CSS classes to spread icon + text horizontally on a button */ -export const horFlexClasses = 'flex flex-row items-center justify-between gap-4' +export const horFlexClasses = + 'tw-flex tw-flex-row tw-items-center tw-justify-between tw-gap-4 tw-w-full' /* * CSS classes to spread icon + text horizontally on a button, only from md upwards */ export const horFlexClassesNoSm = - 'md:flex md:flex-row md:items-center md:justify-between md:gap-4 md-w-full' + 'md:tw-flex md:tw-flex-row md:tw-items-center md:tw-justify-between md:tw-gap-4 tw-w-full' /* * These classes are what makes a link a link @@ -415,6 +417,15 @@ export const optionType = (option) => { return 'constant' } +/* + * Returns a random loading message + * + * @return {string} msg - A random loading message + */ +export function randomLoadingMessage() { + return loadingMessages[Math.floor(Math.random() * loadingMessages.length)] +} + /* * Generic rounding method * diff --git a/packages/utils/src/loading-messages.mjs b/packages/utils/src/loading-messages.mjs new file mode 100644 index 00000000000..35780fb59cc --- /dev/null +++ b/packages/utils/src/loading-messages.mjs @@ -0,0 +1,27 @@ +export const loadingMessages = [ + 'Unfolding ideas...', + 'Teaching hamsters to run faster...', + 'Convincing pixels to get in line...', + 'Warming up the flux capacitor...', + 'Untangling digital spaghetti...', + 'Counting backwards from infinity...', + 'Generating witty loading messages...', + 'Brewing digital coffee...', + 'Herding cats into quantum boxes...', + 'Downloading more RAM...', + 'Dividing by zero...', + 'Spinning up the hamster wheel...', + 'Converting caffeine to code...', + 'Bending the space-time continuum...', + 'Charging laser batteries...', + 'Summoning the data spirits...', + 'Searching for the lost semicolon...', + 'Consulting the digital rolodex...', + 'Calibrating the flux capacitor...', + 'Collecting magic internet points...', + 'Solving P vs NP...', + 'Compressing time and space...', + 'Entering the matrix...', + 'Upgrading to Web 4.0...', + 'Debugging the debugger...', +] diff --git a/sites/org/src/pages/signin/index.js b/sites/org/src/pages/signin/index.js index c6457b90bda..f89293f9d97 100644 --- a/sites/org/src/pages/signin/index.js +++ b/sites/org/src/pages/signin/index.js @@ -15,8 +15,8 @@ export default function SignInPage() { title="Sign In" description="Sign In to your FreeSewing account to unlock all features" > -
-
+
+
diff --git a/sites/org/themes/dark.mjs b/sites/org/themes/dark.mjs index eec5dd99354..d55e7dd3f52 100644 --- a/sites/org/themes/dark.mjs +++ b/sites/org/themes/dark.mjs @@ -24,7 +24,7 @@ export const theme = { 'neutral-focus': colors.neutral['700'], 'neutral-content': colors.neutral['50'], - info: colors.indigo['400'], + info: colors.yellow['400'], success: colors.green['400'], warning: colors.orange['400'], error: colors.red['400'], diff --git a/sites/org/themes/light.mjs b/sites/org/themes/light.mjs index 0bb7061bea8..8b2173060b7 100644 --- a/sites/org/themes/light.mjs +++ b/sites/org/themes/light.mjs @@ -79,9 +79,9 @@ export const theme = { // info: Used rarely, can be another color best somewhat neutral looking // and should work with the default text color - info: colors.indigo['600'], + info: colors.yellow['300'], // Text color on info - 'info-content': colors.neutral[50], + 'info-content': colors.neutral[900], // success: Used rarely, but if it is it's in notifications indicating success // Typically some shade of green