// Dependencies import { control as controlConfig, isDegreeMeasurement, measurements, urls, } from '@freesewing/config' import { measurements as measurementTranslations } from '@freesewing/i18n' import { measurements as designMeasurements } from '@freesewing/collection' import { cloudflareImageUrl, formatMm, horFlexClasses, linkClasses, shortDate, timeAgo, } from '@freesewing/utils' // Context import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus' import { ModalContext } from '@freesewing/react/context/Modal' // Hooks import React, { Fragment, useContext, useEffect, useState } from 'react' import { useAccount } from '@freesewing/react/hooks/useAccount' import { useBackend } from '@freesewing/react/hooks/useBackend' // Components import { Link as WebLink } from '@freesewing/react/components/Link' import { BoolNoIcon, BoolYesIcon, CloneIcon, CompareIcon, CuratedMeasurementsSetIcon, EditIcon, NoIcon, OkIcon, ResetIcon, ShowcaseIcon, UploadIcon, } from '@freesewing/react/components/Icon' import { BookmarkButton, MsetCard } from '@freesewing/react/components/Account' import { DesignInput, ListInput, MarkdownInput, MeasurementInput, PassiveImageInput, StringInput, ToggleInput, } from '@freesewing/react/components/Input' import { Pattern } from '../Pattern/index.mjs' import { DisplayRow } from './shared.mjs' import Markdown from 'react-markdown' import { ModalWrapper } from '@freesewing/react/components/Modal' import { Json } from '@freesewing/react/components/Json' import { Yaml } from '@freesewing/react/components/Yaml' import { Popout } from '@freesewing/react/components/Popout' import { bundlePatternTranslations, draft, flattenFlags } from '../Editor/lib/index.mjs' import { Bonny } from '@freesewing/bonny' import { MiniNote, MiniTip } from '../Mini/index.mjs' /** * Component to show an individual measurements set * * @component * @param {object} props - All Component props * @param {number} props.id - The ID of the measurements set to load * @param {bool} [props.publicOnly = false] - If the set should be used with the backend.getPublicSet method * @param {function} [props.Link = false] - An optional framework-specific Link component to use for client-side routing * @param {function} [measurementHelpProvider = false] - A function that returns a url or action to show help for a specific measurement */ export const Set = ({ id, publicOnly = false, Link = false, measurementHelpProvider = false }) => { if (!Link) Link = WebLink // Hooks const { account, control } = useAccount() const { setLoadingStatus } = useContext(LoadingStatusContext) const backend = useBackend() // Context const { setModal } = useContext(ModalContext) const [filter, setFilter] = useState(false) const [edit, setEdit] = useState(false) const [suggest, setSuggest] = useState(false) const [render, setRender] = useState(false) const [mset, setMset] = useState() // Set fields for editing const [name, setName] = useState(mset?.name) const [image, setImage] = useState(mset?.image) const [isPublic, setIsPublic] = useState(mset?.public ? true : false) const [imperial, setImperial] = useState(mset?.imperial ? true : false) const [notes, setNotes] = useState(mset?.notes || '') const [measies, setMeasies] = useState({}) const [displayAsMetric, setDisplayAsMetric] = useState(mset?.imperial ? false : true) // Effect useEffect(() => { const getSet = async () => { setLoadingStatus([true, 'Contacting the backend']) const [status, body] = await backend.getSet(id) if (status === 200 && body.result === 'success') { setMset(body.set) setName(body.set.name) setImage(body.set.image) setIsPublic(body.set.public ? true : false) setImperial(body.set.imperial ? true : false) setNotes(body.set.notes) setMeasies(body.set.measies) setDisplayAsMetric(body.set.imperial ? false : true) setLoadingStatus([true, 'Measurements set loaded', true, true]) } else setLoadingStatus([true, 'An error occured while contacting the backend', true, false]) } const getPublicSet = async () => { setLoadingStatus([true, 'Contacting the backend']) const [status, body] = await backend.getPublicSet(id) if (status === 200 && body.result === 'success') { const isImperial = body.units === 'imperial' setMset({ ...body, public: true, measies: body.measurements, imperial: isImperial, }) setName(body.name) setImage(body.image) setIsPublic(body.public ? true : false) setImperial(isImperial) setNotes(body.notes) setMeasies(body.measurements) setDisplayAsMetric(!isImperial) setLoadingStatus([true, 'Measurements set loaded', true, true]) } else setLoadingStatus([ true, 'An error occured while loading this measurements set', true, false, ]) } if (id) { if (publicOnly) getPublicSet() else getSet() } }, [id, publicOnly]) const filterMeasurements = () => filter ? designMeasurements[filter].sort() : measurements.sort() if (!id || !mset) return null const updateMeasies = (m, val) => { const newMeasies = { ...measies } newMeasies[m] = val setMeasies(newMeasies) } const save = async () => { setLoadingStatus([true, 'Gathering info']) // Compile data const data = { measies: {} } if (name || name !== mset.name) data.name = name if (image || image !== mset.image) data.img = image if ([true, false].includes(isPublic) && isPublic !== mset.public) data.public = isPublic if ([true, false].includes(imperial) && imperial !== mset.imperial) data.imperial = imperial if (notes || notes !== mset.notes) data.notes = notes // Add measurements for (const m of measurements) { if (measies[m] || measies[m] !== mset.measies[m]) data.measies[m] = measies[m] } setLoadingStatus([true, 'Saving measurements set']) const [status, body] = await backend.updateSet(mset.id, data) if (status === 200 && body.result === 'success') { setMset(body.set) setEdit(false) setLoadingStatus([true, 'Nailed it', true, true]) } else setLoadingStatus([true, 'That did not go as planned. Saving the set failed.', true, false]) } const togglePublic = async () => { setLoadingStatus([true, 'Getting ready']) const [status, body] = await backend.updateSet(mset.id, { public: !mset.public }) if (status === 200 && body.result === 'success') { setMset(body.set) setLoadingStatus([true, 'Alright, done!', true, true]) } else setLoadingStatus([true, 'Backend says no :(', true, false]) } const importSet = async () => { setLoadingStatus([true, 'Importing data']) // Compile data const data = { ...mset, userId: account.id, measies: { ...mset.measies }, } delete data.img const [status, body] = await backend.createSet(data) if (status === 201 && body.result === 'created') { setLoadingStatus([true, 'Loading newly created set', true, true]) window.location = `/account/data/sets/set?id=${body.set.id}` } else setLoadingStatus([true, 'We failed to create this measurements set', true, false]) } const heading = ( <>
Your submission has been registered and will be processed by one of our curators.
It is available at: {url}
> ) } return ( <>To ensure curated measurements sets work for all designs, you need to provide a full set of measurements.
Your measurements set is missing the following measurements:
All measurements are available.
)}Each curated set has a name. You can suggest your own name or a pseudonym.
To allow organizing and presenting our curated sets in a structured way, we organize them by height.
Finally, we need a picture. Please refer to the documentation to see what makes a good picture for a curated measurements set. Documentation
If you would like to add any notes, you can do so here.
To validate and preview a measurement set, all measurements need to be entered.
Your measurements set is missing the following measurements:
Based on your measurements, we estimate your body to be about{' '} {formatMm(pattern.parts[0].front.points.head.y * -1, imperial)} high.
Here is what the automated analysis found:
{Object.entries(flattenFlags(flags)).map(([key, flag]) => { const desc = strings[flag.desc] || flag.desc return (This feature creates a visual preview of your body based on your measurements.
It’s meant to help you spot possible mistakes and better understand how the software sees your measurements, but keep in mind:
Give this set of measurements a name. That will help tell them apart.