From 045edc78d46bc9a878f02e5901c142c04097d22f Mon Sep 17 00:00:00 2001 From: Sourabh Bagde Date: Sat, 15 Nov 2025 05:46:36 -0600 Subject: [PATCH 1/5] added myspace as a platform for announcements component --- src/components/Announcements/index.jsx | 15 +- .../platforms/myspace/Myspace.module.css | 434 +++++++++ .../Announcements/platforms/myspace/index.jsx | 878 ++++++++++++++++++ 3 files changed, 1322 insertions(+), 5 deletions(-) create mode 100644 src/components/Announcements/platforms/myspace/Myspace.module.css create mode 100644 src/components/Announcements/platforms/myspace/index.jsx diff --git a/src/components/Announcements/index.jsx b/src/components/Announcements/index.jsx index 7c92fe3445..106251c7dc 100644 --- a/src/components/Announcements/index.jsx +++ b/src/components/Announcements/index.jsx @@ -16,6 +16,7 @@ import { import { faFacebook, faLinkedin, faMedium } from '@fortawesome/free-brands-svg-icons'; import ReactTooltip from 'react-tooltip'; import EmailPanel from './platforms/email'; +import MyspaceAutoPoster from './platforms/myspace'; function Announcements({ title, email: initialEmail }) { const [activeTab, setActiveTab] = useState('email'); @@ -165,11 +166,15 @@ function Announcements({ title, email: initialEmail }) { 'slashdot', 'blogger', 'truthsocial', - ].map(platform => ( - - - - ))} + ].map(platform => { + const PlatformComposer = + platform === 'myspace' ? MyspaceAutoPoster : SocialMediaComposer; + return ( + + + + ); + })} diff --git a/src/components/Announcements/platforms/myspace/Myspace.module.css b/src/components/Announcements/platforms/myspace/Myspace.module.css new file mode 100644 index 0000000000..5b80ce6a3c --- /dev/null +++ b/src/components/Announcements/platforms/myspace/Myspace.module.css @@ -0,0 +1,434 @@ + + +.myspace-autoposter { + /* max-width: 980px; */ + width: 100%; + margin: 0 auto; + display: grid; + gap: 24px; +} + +.myspace-autoposter.dark { + color: #dbe6ff; +} + +.myspace-autoposter.dark label { + color: #dbe6ff; +} + +.myspace-autoposter.dark p { + color: #dbe6ff; +} + +.myspace-subtabs { + display: flex; + flex-wrap: wrap; + gap: 8px; + margin-bottom: 18px; + border-bottom: 1px solid #ccd4e0; +} + +.myspace-subtab { + padding: 9px 16px; + border-radius: 6px 6px 0 0; + border: 1px solid transparent; + border-bottom: none; + background: #d9d9d9; + color: #333; + font-weight: 600; + cursor: pointer; + transition: all 0.2s ease; +} + +.myspace-subtab:hover { + background: #cfcfcf; +} + +.myspace-subtab.active { + background: #d7ecff; + color: #0d6efd; + border-color: #99c8ff; + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.6); +} + +.myspace-autoposter.dark .myspace-subtab { + border-color: transparent; + background: #2d3c53; + color: #cdd8f6; +} + +.myspace-autoposter.dark .myspace-subtab.active { + background: #1f4a80; + border-color: #1f4a80; + color: #fff; +} + +.myspace-card { + background: #fff; + border: 1px solid #d6dde7; + border-radius: 12px; + padding: 20px 22px; + box-shadow: 0 10px 24px rgba(15, 37, 80, 0.08); + transition: border-color 0.2s ease, box-shadow 0.2s ease; +} + +.myspace-autoposter.dark .myspace-card { + background: #14233a; + border-color: #25354d; + box-shadow: none; +} + +.myspace-card.invalid { + border-color: #d9534f; + box-shadow: 0 0 0 1px rgba(217, 83, 79, 0.18); +} + +.myspace-autoposter.dark .myspace-card.invalid { + border-color: #ff7b72; + box-shadow: none; +} + +.myspace-grid { + display: grid; + gap: 20px; + grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); +} + +.myspace-field__header { + display: flex; + justify-content: space-between; + align-items: baseline; + gap: 12px; + margin-bottom: 8px; +} + +.myspace-field__meta { + font-size: 0.85rem; + color: #6c757d; +} + +.myspace-field__meta.invalid { + color: #d9534f; +} + +.myspace-autoposter.dark .myspace-field__meta { + color: #9aa9c6; +} + +.myspace-field__required { + color: #d9534f; + margin-left: 4px; +} + +.myspace-autoposter.dark .myspace-field__required { + color: #ff9384; +} + +.myspace-field__input { + width: 100%; + border: 1px solid #c7d1e5; + border-radius: 8px; + padding: 12px 14px; + font-size: 0.95rem; + background: #fff; + color: #1b1f29; +} + +.myspace-autoposter.dark .myspace-field__input { + background: #0f1c2d; + border-color: #2b3b55; + color: #e4edff; +} + +.myspace-field__input--invalid { + border-color: #d9534f; + box-shadow: 0 0 0 1px rgba(217, 83, 79, 0.2); +} + +.myspace-autoposter.dark .myspace-field__input--invalid { + border-color: #ff9384; + box-shadow: 0 0 0 1px rgba(255, 147, 132, 0.3); +} + +.myspace-field__textarea { + resize: vertical; + min-height: 110px; + white-space: pre-wrap; + overflow-wrap: anywhere; + word-break: break-word; +} + +.myspace-field__error { + color: #d9534f; + font-size: 0.85rem; + margin-top: 8px; +} + +.myspace-autoposter.dark .myspace-field__error { + color: #ff9384; +} + +.myspace-field__hint { + color: #6c757d; + font-size: 0.85rem; + margin-top: 8px; +} + +.myspace-autoposter.dark .myspace-field__hint { + color: #9aa9c6; +} + +.myspace-chips { + display: flex; + flex-wrap: wrap; + gap: 8px; + margin-top: 10px; +} + +.myspace-chip { + display: inline-flex; + align-items: center; + justify-content: center; + padding: 4px 10px; + border-radius: 999px; + background: #e9efff; + color: #1c3f82; + font-size: 0.8rem; + font-weight: 600; + max-width: 100%; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + gap: 6px; +} + +.myspace-autoposter.dark .myspace-chip { + background: rgba(13, 110, 253, 0.22); + color: #cfe0ff; +} + +.myspace-chip__label { + overflow: hidden; + text-overflow: ellipsis; +} + +.myspace-chip__clear { + border: none; + background: transparent; + cursor: pointer; + color: #d9534f; + font-size: 0.9rem; + padding: 0 4px; + line-height: 1; + display: inline-flex; + align-items: center; + justify-content: center; + transition: color 0.2s ease; +} + +.myspace-chip__clear:hover, +.myspace-chip__clear:focus { + color: #b7322d; + background: transparent; +} + +.myspace-autoposter.dark .myspace-chip__clear { + color: #ff9384; +} + +.myspace-preview__header { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + margin-bottom: 12px; +} + +.myspace-preview__actions { + display: flex; + flex-wrap: wrap; + gap: 10px; + justify-content: flex-end; +} + +.myspace-preview__body { + white-space: pre-wrap; + font-family: 'SFMono-Regular', Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', + monospace; + background: #f8f9fb; + border: 1px solid #d6dde7; + border-radius: 8px; + padding: 16px; + color: #27324b; + max-height: 240px; + overflow: auto; + overflow-wrap: anywhere; + word-break: break-word; +} + +.myspace-autoposter.dark .myspace-preview__body { + background: #0f1c2d; + border-color: #2b3b55; + color: #e4edff; +} + +.myspace-preview__hint { + font-size: 0.85rem; + color: #6c757d; + margin-top: 12px; +} + +.myspace-autoposter.dark .myspace-preview__hint { + color: #9aa9c6; +} + +.myspace-card--scheduler { + max-width: 720px; +} + +.myspace-scheduler__grid { + display: grid; + gap: 20px; + grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr); + align-items: start; +} + +.myspace-card--saved { + max-width: 100%; +} + +.myspace-scheduler__note { + font-size: 0.85rem; + color: #6c757d; + margin-top: 12px; +} + +.myspace-autoposter.dark .myspace-scheduler__note { + color: #9aa9c6; +} + +.myspace-scheduler__controls { + display: flex; + flex-wrap: wrap; + gap: 16px; + margin: 18px 0; +} + +.myspace-scheduler__field { + flex: 1 1 200px; + display: flex; + flex-direction: column; + gap: 8px; +} + +.myspace-scheduler__textarea { + min-height: 220px; +} + +.myspace-scheduler__actions { + display: flex; + flex-wrap: wrap; + gap: 12px; + margin-top: 18px; +} + +.myspace-scheduler__empty { + font-size: 0.9rem; + color: #6c757d; + margin: 8px 0 0; +} + +.myspace-autoposter.dark .myspace-scheduler__empty { + color: #9aa9c6; +} + +.myspace-saved__list { + display: flex; + flex-direction: column; + gap: 12px; + margin-top: 18px; +} + +.myspace-saved__item { + border: 1px solid #d6dde7; + border-radius: 10px; + background: #f4f7fd; + padding: 12px 14px; + display: flex; + flex-direction: column; + gap: 10px; +} + +.myspace-autoposter.dark .myspace-saved__item { + border-color: #2b3b55; + background: #0f1c2d; +} + +.myspace-saved__item--active { + border-color: #0d6efd; + box-shadow: 0 0 0 1px rgba(13, 110, 253, 0.24); +} + +.myspace-autoposter.dark .myspace-saved__item--active { + border-color: #4785ff; + box-shadow: 0 0 0 1px rgba(71, 133, 255, 0.32); +} + +.myspace-saved__header { + display: flex; + justify-content: space-between; + align-items: baseline; + gap: 12px; +} + +.myspace-saved__title { + font-size: 1rem; + font-weight: 600; + margin: 0; + color: #1b1f29; +} + +.myspace-autoposter.dark .myspace-saved__title { + color: #dbe6ff; +} + +.myspace-saved__meta { + font-size: 0.85rem; + color: #6c757d; +} + +.myspace-autoposter.dark .myspace-saved__meta { + color: #9aa9c6; +} + +.myspace-saved__excerpt { + font-size: 0.9rem; + color: #4f5a73; + margin: 0; +} + +.myspace-autoposter.dark .myspace-saved__excerpt { + color: #cfd9f8; +} + +.myspace-saved__actions { + display: flex; + flex-wrap: wrap; + gap: 10px; +} + +@media (max-width: 960px) { + .myspace-scheduler__grid { + grid-template-columns: 1fr; + } +} + +@media (max-width: 640px) { + .myspace-autoposter { + gap: 18px; + } + + .myspace-card { + padding: 18px; + } +} diff --git a/src/components/Announcements/platforms/myspace/index.jsx b/src/components/Announcements/platforms/myspace/index.jsx new file mode 100644 index 0000000000..c11416c51d --- /dev/null +++ b/src/components/Announcements/platforms/myspace/index.jsx @@ -0,0 +1,878 @@ +import React, { useMemo, useState } from 'react'; +import PropTypes from 'prop-types'; +import classNames from 'classnames'; +import { useSelector } from 'react-redux'; +import { toast } from 'react-toastify'; + +import styles from './Myspace.module.css'; + +const HEADLINE_MIN = 12; +const HEADLINE_MAX = 95; +const SUMMARY_MIN = 80; +const STOP_WORDS = new Set([ + 'about', + 'after', + 'also', + 'another', + 'because', + 'been', + 'being', + 'between', + 'can', + 'could', + 'during', + 'each', + 'from', + 'have', + 'into', + 'more', + 'other', + 'over', + 'since', + 'some', + 'than', + 'that', + 'their', + 'there', + 'these', + 'they', + 'this', + 'through', + 'under', + 'until', + 'where', + 'which', + 'while', + 'with', + 'within', +]); + +const sanitizeTags = text => + text + .split(',') + .map(tag => + tag + .trim() + .toLowerCase() + .replace(/\s+/g, '-') + .replace(/[^a-z0-9-]/g, ''), + ) + .filter(Boolean); + +const slugify = text => + text + .toLowerCase() + .replace(/[^a-z0-9\s-]/g, '') + .replace(/[\s_-]+/g, '-') + .replace(/^-+|-+$/g, '') + .trim(); + +const extractTagCandidates = (headline, summary, existing) => { + if (Array.isArray(existing) && existing.length) return existing.slice(0, 6); + const corpus = `${headline} ${summary}`.toLowerCase(); + const words = corpus.match(/[a-z0-9']+/g) || []; + const candidates = []; + for (const raw of words) { + const cleaned = raw.replace(/'/g, ''); + if (cleaned.length < 4) continue; + if (STOP_WORDS.has(cleaned)) continue; + if (!candidates.includes(cleaned)) candidates.push(cleaned); + if (candidates.length === 6) break; + } + return candidates; +}; + +const buildPreview = ({ headline, sourceUrl, dept, tags, intro }) => + `Headline\n${headline?.trim() || '—'}\n\nSource URL\n${sourceUrl?.trim() || + '—'}\n\nDept\n${dept?.trim() || '—'}\n\nTags\n${ + tags.length ? tags.join(', ') : '—' + }\n\nIntro / Summary\n${intro?.trim() || '—'}\n`; + +const padTimeUnit = value => String(value).padStart(2, '0'); + +const formatLocalDate = date => + `${date.getFullYear()}-${padTimeUnit(date.getMonth() + 1)}-${padTimeUnit(date.getDate())}`; + +const formatLocalTime = date => `${padTimeUnit(date.getHours())}:${padTimeUnit(date.getMinutes())}`; + +const createScheduleId = () => + `schedule-${Date.now().toString(36)}-${Math.random() + .toString(36) + .slice(2, 8)}`; + +const formatDisplayDateTime = (dateString, timeString) => { + if (!dateString) return '—'; + try { + const composed = `${dateString}T${timeString || '00:00'}`; + const parsed = new Date(composed); + if (Number.isNaN(parsed.getTime())) { + return `${dateString}${timeString ? `, ${timeString}` : ''}`; + } + const formattedDate = parsed.toLocaleDateString(undefined, { + month: 'short', + day: 'numeric', + year: 'numeric', + }); + const formattedTime = timeString + ? parsed.toLocaleTimeString(undefined, { + hour: '2-digit', + minute: '2-digit', + }) + : ''; + return formattedTime ? `${formattedDate} • ${formattedTime}` : formattedDate; + } catch (error) { + return `${dateString}${timeString ? `, ${timeString}` : ''}`; + } +}; + +const topCardActions = () => ({ + display: 'flex', + flexWrap: 'wrap', + gap: '12px', + marginTop: '16px', +}); + +const buttonStyle = (variant, darkMode) => { + const base = { + borderRadius: '999px', + border: 'none', + cursor: 'pointer', + fontWeight: 600, + padding: '10px 18px', + transition: 'filter 0.2s ease', + }; + if (variant === 'primary') { + return { + ...base, + backgroundColor: '#0d6efd', + color: '#fff', + }; + } + if (variant === 'outline') { + return { + ...base, + backgroundColor: 'transparent', + color: darkMode ? '#9bb5ff' : '#0d6efd', + border: `1px solid ${darkMode ? '#3d4d6d' : '#0d6efd'}`, + }; + } + return { + ...base, + backgroundColor: darkMode ? '#1c2b44' : '#e9efff', + color: darkMode ? '#cfd9f8' : '#1c3f82', + }; +}; + +const fieldActionRow = { + display: 'flex', + flexWrap: 'wrap', + gap: '10px', + marginTop: '12px', +}; + +function MyspaceAutoPoster({ platform }) { + const darkMode = useSelector(state => state.theme.darkMode); + + const [headline, setHeadline] = useState(''); + const [sourceUrl, setSourceUrl] = useState(''); + const [dept, setDept] = useState(''); + const [tagsText, setTagsText] = useState(''); + const [intro, setIntro] = useState(''); + const [activeSubTab, setActiveSubTab] = useState('make'); + const [scheduledDraft, setScheduledDraft] = useState(''); + const [scheduledDate, setScheduledDate] = useState(() => formatLocalDate(new Date())); + const [scheduledTime, setScheduledTime] = useState(() => formatLocalTime(new Date())); + const [savedSchedules, setSavedSchedules] = useState([]); + const [editingScheduleId, setEditingScheduleId] = useState(null); + const [scheduleAttemptedSave, setScheduleAttemptedSave] = useState(false); + + const subTabs = useMemo( + () => [ + { id: 'make', label: '📝 Make Post' }, + { id: 'schedule', label: '⏰ Scheduled Post' }, + ], + [], + ); + + const tags = useMemo(() => sanitizeTags(tagsText), [tagsText]); + + const trimmedHeadline = headline.trim(); + const trimmedUrl = sourceUrl.trim(); + const trimmedDept = dept.trim(); + const trimmedIntro = intro.trim(); + + const headlineInRange = + trimmedHeadline.length >= HEADLINE_MIN && trimmedHeadline.length <= HEADLINE_MAX; + const urlValid = /^https?:\/\//i.test(trimmedUrl); + const deptValid = trimmedDept.length >= 3; + const summaryValid = trimmedIntro.length >= SUMMARY_MIN; + const tagsValid = tags.length > 0; + + const readyToCopy = headlineInRange && urlValid && deptValid && summaryValid && tagsValid; + + const highlightHeadline = trimmedHeadline.length > 0 && !headlineInRange; + const highlightUrl = trimmedUrl.length > 0 && !urlValid; + const highlightDept = trimmedDept.length > 0 && !deptValid; + const highlightSummary = trimmedIntro.length > 0 && !summaryValid; + + const hasAnyInput = Boolean( + trimmedHeadline || trimmedUrl || trimmedDept || trimmedIntro || tagsText.trim(), + ); + + const preview = useMemo(() => { + if (!hasAnyInput) return ''; + return buildPreview({ headline, sourceUrl, dept, tags, intro }); + }, [dept, headline, hasAnyInput, intro, sourceUrl, tags]); + const scheduleHasDraft = scheduledDraft.trim().length > 0; + const editingSchedule = useMemo( + () => savedSchedules.find(schedule => schedule.id === editingScheduleId) || null, + [editingScheduleId, savedSchedules], + ); + + const copyText = async (text, label) => { + const value = text?.trim(); + if (!value) { + toast.warn(`Nothing to copy for ${label}.`); + return; + } + try { + await navigator.clipboard.writeText(value); + toast.success(`${label} copied to clipboard`); + } catch (error) { + toast.error(`Could not copy ${label.toLowerCase()}.`); + } + }; + + const handleReset = () => { + setHeadline(''); + setSourceUrl(''); + setDept(''); + setTagsText(''); + setIntro(''); + }; + + const openMyspaceSubmit = () => { + if (typeof window !== 'undefined') { + window.open('https://myspace.com/home', '_blank', 'noopener,noreferrer'); + } + }; + + const handleScheduleClick = () => { + if (!hasAnyInput) { + toast.error('Nothing to schedule yet. Add details in Make Post first.'); + return; + } + const missingFields = []; + if (!trimmedHeadline) missingFields.push('Headline'); + if (!trimmedUrl) missingFields.push('Source URL'); + if (!trimmedDept) missingFields.push('Dept'); + if (tags.length === 0) missingFields.push('Tags'); + if (!trimmedIntro) missingFields.push('Intro / Summary'); + if (missingFields.length > 0) { + toast.error(`Add ${missingFields.join(', ')} before scheduling.`); + return; + } + const now = new Date(); + setScheduledDate(formatLocalDate(now)); + setScheduledTime(formatLocalTime(now)); + setScheduledDraft(preview); + setScheduleAttemptedSave(false); + setActiveSubTab('schedule'); + toast.success('Draft moved to Schedule tab.'); + }; + + const removeTag = tagToRemove => { + const remaining = tags.filter(tag => tag !== tagToRemove); + setTagsText(remaining.join(', ')); + }; + const now = new Date(); + const today = formatLocalDate(now); + const currentTime = formatLocalTime(now); + const scheduleTimeMin = scheduledDate === today ? currentTime : '00:00'; + + const handleScheduleDateChange = event => { + const nextDateRaw = event.target.value; + if (!nextDateRaw) return; + const nextDate = nextDateRaw < today ? today : nextDateRaw; + setScheduledDate(nextDate); + setScheduleAttemptedSave(false); + if (nextDate === today) { + const refreshedNow = new Date(); + const refreshedTime = formatLocalTime(refreshedNow); + setScheduledTime(prev => (prev && prev >= refreshedTime ? prev : refreshedTime)); + } + }; + + const handleScheduleTimeChange = event => { + const nextTimeRaw = event.target.value; + if (!nextTimeRaw) return; + if (scheduledDate === today) { + const refreshedNow = new Date(); + const refreshedTime = formatLocalTime(refreshedNow); + setScheduledTime(nextTimeRaw >= refreshedTime ? nextTimeRaw : refreshedTime); + setScheduleAttemptedSave(false); + return; + } + setScheduledTime(nextTimeRaw); + setScheduleAttemptedSave(false); + }; + + const handleBackToMake = () => { + setScheduleAttemptedSave(false); + setActiveSubTab('make'); + }; + + const handleSaveSchedule = () => { + setScheduleAttemptedSave(true); + if (!scheduleHasDraft) { + toast.warn('Add content to the schedule before saving.'); + return; + } + if (!scheduledDate || !scheduledTime) { + toast.error('Choose a schedule date and time.'); + return; + } + const isEditing = Boolean(editingScheduleId); + const recordId = isEditing ? editingScheduleId : createScheduleId(); + const record = { + id: recordId, + headline, + sourceUrl, + dept, + tagsText, + tags: [...tags], + intro, + scheduledDraft: scheduledDraft.trim(), + scheduledDate, + scheduledTime, + updatedAt: new Date().toISOString(), + }; + setSavedSchedules(prev => { + const remaining = prev.filter(item => item.id !== record.id); + return [record, ...remaining]; + }); + const toastMessage = isEditing ? 'Scheduled post updated.' : 'Scheduled post saved.'; + toast.success(toastMessage); + handleReset(); + setScheduledDraft(''); + setScheduledDate(''); + setScheduledTime(''); + setScheduleAttemptedSave(false); + setEditingScheduleId(null); + setActiveSubTab('make'); + }; + + const handleEditSchedule = scheduleId => { + const target = savedSchedules.find(schedule => schedule.id === scheduleId); + if (!target) return; + const refreshedToday = formatLocalDate(new Date()); + let nextDate = target.scheduledDate || refreshedToday; + if (nextDate < refreshedToday) { + nextDate = refreshedToday; + } + let nextTime = target.scheduledTime || '00:00'; + if (nextDate === refreshedToday) { + const refreshedNow = new Date(); + const refreshedTime = formatLocalTime(refreshedNow); + if (!nextTime || nextTime < refreshedTime) { + nextTime = refreshedTime; + } + } + setHeadline(target.headline || ''); + setSourceUrl(target.sourceUrl || ''); + setDept(target.dept || ''); + setTagsText(target.tagsText || ''); + setIntro(target.intro || ''); + setScheduledDraft(target.scheduledDraft || ''); + setScheduledDate(nextDate); + setScheduledTime(nextTime); + setScheduleAttemptedSave(false); + setEditingScheduleId(target.id); + setActiveSubTab('schedule'); + toast.info('Loaded scheduled post for editing.'); + }; + + return ( +
+
+ {subTabs.map(({ id, label }) => ( + + ))} +
+ + {activeSubTab === 'make' ? ( + <> +
+

Myspace Auto-Poster

+

+ Myspace submissions require five pieces: a strong headline, the source URL, a short + department slug, relevant tags, and an 80+ character summary. Use the tools below to + build a ready-to-submit draft, then copy it (or open myspace.com/home) to finish the + manual submission. +

+
+ +
+
+ +
+
+
+ + + {headline.trim().length}/{HEADLINE_MAX} + +
+ setHeadline(e.target.value)} + className={styles['myspace-field__input']} + placeholder="e.g. Open Source Volunteers Deliver Weekly Progress Platform" + /> + {!trimmedHeadline && ( +

+ Add a headline with at least {HEADLINE_MIN} characters and keep it below{' '} + {HEADLINE_MAX}. +

+ )} + {highlightHeadline && ( +

+ Aim for {HEADLINE_MIN}-{HEADLINE_MAX} characters so the headline fits Myspace’s + front page. +

+ )} +
+ + +
+
+ +
+
+ +
+ setSourceUrl(e.target.value)} + className={styles['myspace-field__input']} + placeholder="https://" + /> + {!trimmedUrl && ( +

+ Paste the canonical article or project URL. +

+ )} + {highlightUrl && ( +

+ Myspace only accepts fully qualified HTTP(S) links. +

+ )} +
+ +
+
+ +
+
+ + short slug +
+ setDept(e.target.value.toLowerCase())} + className={styles['myspace-field__input']} + placeholder="e.g. volunteer-tech" + /> + {!trimmedDept && ( +

+ Set the playful department label Myspace shows under the headline. +

+ )} + {highlightDept && ( +

+ Use a short slug-style phrase with lowercase letters and dashes. +

+ )} +
+ + +
+
+ +
+
+ + comma separated +
+