diff --git a/README.md b/README.md index 1ccacee8..322bf15b 100644 --- a/README.md +++ b/README.md @@ -83,6 +83,11 @@ npm install --save-dev svelte-highlight typescript-svelte-plugin npm install flowbite flowbite-svelte tailwind-merge @popperjs/core ``` +More packages related to the Tiptap editor +```bash +npm install @tiptap/extension-link @tiptap/extension-bullet-list @tiptap/extension-history @tiptap/extension-typography @tiptap/extension-underline +``` + ## Build ## Environment file diff --git a/package-lock.json b/package-lock.json index f6859a14..95947a60 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,6 +20,7 @@ "@tiptap/extension-paragraph": "^2.10.2", "@tiptap/extension-text": "^2.10.2", "@tiptap/extension-typography": "^2.10.2", + "@tiptap/extension-underline": "^2.10.3", "@tiptap/pm": "^2.10.2", "@tiptap/starter-kit": "^2.10.2", "axios": "^1.7.0", @@ -1728,6 +1729,19 @@ "@tiptap/core": "^2.7.0" } }, + "node_modules/@tiptap/extension-underline": { + "version": "2.10.3", + "resolved": "https://registry.npmjs.org/@tiptap/extension-underline/-/extension-underline-2.10.3.tgz", + "integrity": "sha512-VeGs0jeNiTnXddHHJEgOc/sKljZiyTEgSSuqMmsBACrr9aGFXbLTgKTvNjkZ9WzSnu7LwgJuBrwEhg8yYixUyQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, "node_modules/@tiptap/pm": { "version": "2.10.2", "resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-2.10.2.tgz", diff --git a/package.json b/package.json index b4038caf..5d2226a4 100644 --- a/package.json +++ b/package.json @@ -67,6 +67,7 @@ "@tiptap/extension-paragraph": "^2.10.2", "@tiptap/extension-text": "^2.10.2", "@tiptap/extension-typography": "^2.10.2", + "@tiptap/extension-underline": "^2.10.3", "@tiptap/pm": "^2.10.2", "@tiptap/starter-kit": "^2.10.2", "axios": "^1.7.0", diff --git a/src/lib/ae_events/ae_events__event.ts b/src/lib/ae_events/ae_events__event.ts index b285d012..65371122 100644 --- a/src/lib/ae_events/ae_events__event.ts +++ b/src/lib/ae_events/ae_events__event.ts @@ -331,7 +331,7 @@ export async function qry_ae_obj_li__event( } -// Updated 2024-09-25 +// Updated 2024-12-02 export async function create_ae_obj__event( { api_cfg, @@ -371,7 +371,8 @@ export async function create_ae_obj__event( db_save_ae_obj_li__event( { obj_type: 'event', - obj_li: [event_obj_create_result] + obj_li: [event_obj_create_result], + log_lvl: log_lvl }); } return event_obj_create_result; diff --git a/src/lib/element_tiptap_editor.scss b/src/lib/element_tiptap_editor.scss index 7ad642cc..9e2a54c9 100644 --- a/src/lib/element_tiptap_editor.scss +++ b/src/lib/element_tiptap_editor.scss @@ -1,102 +1,113 @@ /* Basic editor styles */ .tiptap { -:first-child { - margin-top: 0; -} - -/* List styles */ -ul { - list-style-type: disc; - margin-left: 1.5rem; - // border: solid thin red; -} -ol { - list-style-type: decimal; - margin-left: 1.5rem; - // border: solid thin red; -} - -ul, -ol { - padding: 0 1rem; - margin: 1.25rem 1rem 1.25rem 0.4rem; - - li p { - margin-top: 0.25em; - margin-bottom: 0.25em; + :first-child { + margin-top: 0; } -} -/* Heading styles */ -h1, -h2, -h3, -h4, -h5, -h6 { - line-height: 1.1; - margin-top: 2.5rem; - text-wrap: pretty; -} + /* Link styles */ + a { + color: var(--purple); + cursor: pointer; + text-decoration: underline; -h1, -h2 { - margin-top: 3.5rem; - margin-bottom: 1.5rem; -} + &:hover { + color: var(--purple-contrast); + } + } -h1 { - font-size: 1.4rem; -} + /* List styles */ + ul { + list-style-type: disc; + margin-left: 1.5rem; + // border: solid thin red; + } + ol { + list-style-type: decimal; + margin-left: 1.5rem; + // border: solid thin red; + } -h2 { - font-size: 1.2rem; -} + ul, + ol { + padding: 0 1rem; + margin: 1.25rem 1rem 1.25rem 0.4rem; -h3 { - font-size: 1.1rem; -} + li p { + margin-top: 0.25em; + margin-bottom: 0.25em; + } + } -h4, -h5, -h6 { - font-size: 1rem; -} + /* Heading styles */ + h1, + h2, + h3, + h4, + h5, + h6 { + line-height: 1.1; + margin-top: 2.5rem; + text-wrap: pretty; + } -/* Code and preformatted text styles */ -code { - background-color: var(--purple-light); - border-radius: 0.4rem; - color: var(--black); - font-size: 0.85rem; - padding: 0.25em 0.3em; -} + h1, + h2 { + margin-top: 3.5rem; + margin-bottom: 1.5rem; + } -pre { - background: var(--black); - border-radius: 0.5rem; - color: var(--white); - font-family: 'JetBrainsMono', monospace; - margin: 1.5rem 0; - padding: 0.75rem 1rem; + h1 { + font-size: 1.4rem; + } + h2 { + font-size: 1.2rem; + } + + h3 { + font-size: 1.1rem; + } + + h4, + h5, + h6 { + font-size: 1rem; + } + + /* Code and preformatted text styles */ code { - background: none; - color: inherit; - font-size: 0.8rem; - padding: 0; + background-color: var(--purple-light); + border-radius: 0.4rem; + color: var(--black); + font-size: 0.85rem; + padding: 0.25em 0.3em; } -} -blockquote { - border-left: 3px solid var(--gray-3); - margin: 1.5rem 0; - padding-left: 1rem; -} + pre { + background: var(--black); + border-radius: 0.5rem; + color: var(--white); + font-family: 'JetBrainsMono', monospace; + margin: 1.5rem 0; + padding: 0.75rem 1rem; -hr { - border: none; - border-top: 1px solid var(--gray-2); - margin: 2rem 0; -} + code { + background: none; + color: inherit; + font-size: 0.8rem; + padding: 0; + } + } + + blockquote { + border-left: 3px solid var(--gray-3); + margin: 1.5rem 0; + padding-left: 1rem; + } + + hr { + border: none; + border-top: 1px solid var(--gray-2); + margin: 2rem 0; + } } \ No newline at end of file diff --git a/src/lib/element_tiptap_editor.svelte b/src/lib/element_tiptap_editor.svelte index d2484e28..d21e4c60 100644 --- a/src/lib/element_tiptap_editor.svelte +++ b/src/lib/element_tiptap_editor.svelte @@ -24,6 +24,7 @@ import Strike from '@tiptap/extension-strike'; import Text from '@tiptap/extension-text'; import TextStyle from '@tiptap/extension-text-style'; import Typography from '@tiptap/extension-typography'; +import Underline from '@tiptap/extension-underline'; import "./element_tiptap_editor.scss"; @@ -125,10 +126,11 @@ onMount(() => { CodeBlock, // part of StarterKit Italic, // part of StarterKit Strike, // part of StarterKit + Underline, // part of StarterKit BulletList, // part of StarterKit - Color.configure({ types: [TextStyle.name, ListItem.name] }), - TextStyle.configure({ types: [ListItem.name] }), - Heading.configure({ levels: [1, 2, 3, 4, 5, 6] }), + // Color.configure({ types: [TextStyle.name, ListItem.name] }), + // TextStyle.configure({ types: [ListItem.name] }), + // Heading.configure({ levels: [1, 2, 3, 4, 5, 6] }), Highlight, History.configure({ depth: 100, @@ -141,56 +143,56 @@ onMount(() => { protocols: ['http', 'https'], isAllowedUri: (url, ctx) => { try { - // construct URL - const parsedUrl = url.includes(':') ? new URL(url) : new URL(`${ctx.defaultProtocol}://${url}`) + // construct URL + const parsedUrl = url.includes(':') ? new URL(url) : new URL(`${ctx.defaultProtocol}://${url}`) - // use default validation - if (!ctx.defaultValidate(parsedUrl.href)) { + // use default validation + if (!ctx.defaultValidate(parsedUrl.href)) { + return false + } + + // disallowed protocols + const disallowedProtocols = ['ftp', 'file', 'mailto'] + const protocol = parsedUrl.protocol.replace(':', '') + + if (disallowedProtocols.includes(protocol)) { + return false + } + + // only allow protocols specified in ctx.protocols + const allowedProtocols = ctx.protocols.map(p => (typeof p === 'string' ? p : p.scheme)) + + if (!allowedProtocols.includes(protocol)) { + return false + } + + // disallowed domains + const disallowedDomains = ['example-phishing.com', 'malicious-site.net'] + const domain = parsedUrl.hostname + + if (disallowedDomains.includes(domain)) { + return false + } + + // all checks have passed + return true + } catch (error) { return false } - - // disallowed protocols - const disallowedProtocols = ['ftp', 'file', 'mailto'] - const protocol = parsedUrl.protocol.replace(':', '') - - if (disallowedProtocols.includes(protocol)) { - return false - } - - // only allow protocols specified in ctx.protocols - const allowedProtocols = ctx.protocols.map(p => (typeof p === 'string' ? p : p.scheme)) - - if (!allowedProtocols.includes(protocol)) { - return false - } - - // disallowed domains - const disallowedDomains = ['example-phishing.com', 'malicious-site.net'] - const domain = parsedUrl.hostname - - if (disallowedDomains.includes(domain)) { - return false - } - - // all checks have passed - return true - } catch (error) { - return false - } }, shouldAutoLink: url => { try { - // construct URL - const parsedUrl = url.includes(':') ? new URL(url) : new URL(`https://${url}`) + // construct URL + const parsedUrl = url.includes(':') ? new URL(url) : new URL(`https://${url}`) - // only auto-link if the domain is not in the disallowed list - const disallowedDomains = ['example-no-autolink.com', 'another-no-autolink.com'] - const domain = parsedUrl.hostname + // only auto-link if the domain is not in the disallowed list + const disallowedDomains = ['example-no-autolink.com', 'another-no-autolink.com'] + const domain = parsedUrl.hostname - return !disallowedDomains.includes(domain) - } catch (error) { - return false - } + return !disallowedDomains.includes(domain) + } catch (error) { + return false + } }, }), ListItem, @@ -201,18 +203,20 @@ onMount(() => { Typography, ], content: html_text, - onTransaction: () => { + onTransaction: ({ editor, transaction }) => { + // console.log('onTransaction'); // force re-render so `editor.isActive` works as expected - editor = editor; + // editor = editor; - let updated_html = editor.getHTML(); - if (updated_html == '

') { - new_html = ''; - } else { - new_html = updated_html; - } + // let updated_html = editor.getHTML(); + // if (updated_html == '

') { + // new_html = ''; + // } else { + // new_html = updated_html; + // } }, onUpdate: ({ editor }) => { + console.log('onUpdate', editor.getHTML()); let updated_html = editor.getHTML(); if (updated_html == '

') { new_html = ''; diff --git a/src/routes/idaa/(idaa)/bb/ae_idaa_comp__post_comment_obj_id_edit.svelte b/src/routes/idaa/(idaa)/bb/ae_idaa_comp__post_comment_obj_id_edit.svelte index 7d307723..ea64b79b 100644 --- a/src/routes/idaa/(idaa)/bb/ae_idaa_comp__post_comment_obj_id_edit.svelte +++ b/src/routes/idaa/(idaa)/bb/ae_idaa_comp__post_comment_obj_id_edit.svelte @@ -330,7 +330,7 @@ function send_poster_notification_email() { diff --git a/src/routes/idaa/(idaa)/bb/ae_idaa_comp__post_obj_id_edit.svelte b/src/routes/idaa/(idaa)/bb/ae_idaa_comp__post_obj_id_edit.svelte index 0872f012..eae65d69 100644 --- a/src/routes/idaa/(idaa)/bb/ae_idaa_comp__post_obj_id_edit.svelte +++ b/src/routes/idaa/(idaa)/bb/ae_idaa_comp__post_obj_id_edit.svelte @@ -40,7 +40,7 @@ async function handle_submit_form(event: any) { console.log(form_data); } - // Form Post object data incoming + // Form Post object data incoming let post_di = ae_util.extract_prefixed_form_data({prefix: null, form_data: form_data, trim_values: true, bool_tf_str: true, log_lvl: log_lvl}); // console.log(post_di); @@ -123,6 +123,9 @@ async function handle_submit_form(event: any) { return false; } + if (log_lvl) { + console.log('post_obj_create_result:', post_obj_create_result); + } $idaa_slct.post_id = post_obj_create_result.post_id_random; $idaa_slct.post_obj = post_obj_create_result; @@ -143,7 +146,9 @@ async function handle_submit_form(event: any) { }); return prom_api__post_obj; + } else { + prom_api__post_obj = posts_func.update_ae_obj__post({ api_cfg: $ae_api, post_id: $idaa_slct.post_id, @@ -177,6 +182,7 @@ async function handle_submit_form(event: any) { }); return prom_api__post_obj; + } } @@ -349,13 +355,33 @@ function send_staff_notification_email() { - + {#if $ae_loc.administrator_access} + + {:else} + + {/if} diff --git a/src/routes/idaa/(idaa)/recovery_meetings/+page.svelte b/src/routes/idaa/(idaa)/recovery_meetings/+page.svelte index b8b61461..69b5bb28 100644 --- a/src/routes/idaa/(idaa)/recovery_meetings/+page.svelte +++ b/src/routes/idaa/(idaa)/recovery_meetings/+page.svelte @@ -300,8 +300,7 @@ $: if ($idaa_trig.event_li_qry) { - - +

- {#if $ae_loc.trusted_access || $lq__event_obj?.external_person_id === $idaa_loc.novi_uuid || $lq__event_obj?.contact_li_json[0]?.email === $idaa_loc.novi_email} - - - - {/if} + $idaa_sess.recovery_meetings.show__modal_edit = false; + $idaa_sess.recovery_meetings.show__modal_view = true; + }} + class="btn btn-sm variant-ghost-warning hover:variant-filled-warning transition" + title={`View meeting: ${$lq__event_obj?.name}`} + > + Cancel Edit + + + {/if} - Edit Meeting: {$lq__event_obj?.name}

+ + {$lq__event_obj?.name ?? 'New Recovery Meeting'} +
@@ -361,6 +362,7 @@ $: if ($idaa_trig.event_li_qry) {
+ Edit {/if} - {$lq__event_obj?.name} + + {$lq__event_obj?.name ?? '-- not set'} @@ -401,4 +404,4 @@ $: if ($idaa_trig.event_li_qry) { lq__event_obj={lq__event_obj} /> - \ No newline at end of file + diff --git a/src/routes/idaa/(idaa)/recovery_meetings/ae_idaa_comp__event_obj_id_edit.svelte b/src/routes/idaa/(idaa)/recovery_meetings/ae_idaa_comp__event_obj_id_edit.svelte index 1956e397..737a3660 100644 --- a/src/routes/idaa/(idaa)/recovery_meetings/ae_idaa_comp__event_obj_id_edit.svelte +++ b/src/routes/idaa/(idaa)/recovery_meetings/ae_idaa_comp__event_obj_id_edit.svelte @@ -238,12 +238,16 @@ async function handle_submit_form(event: any) { disable_submit_btn = true; let form_data = new FormData(event.target); - console.log(form_data); + if (log_lvl) { + console.log(form_data); + } - let event_meeting_fd = ae_util.extract_prefixed_form_data({prefix: null, form_data: form_data, trim_values: true, bool_tf_str: true, log_lvl: 0}); - console.log(event_meeting_fd); + // Form Event object data incoming + let event_meeting_fd = ae_util.extract_prefixed_form_data({prefix: null, form_data: form_data, trim_values: true, bool_tf_str: true, log_lvl: log_lvl}); + // console.log(event_meeting_fd); - let event_do: key_val = {}; // Data out for API object + // Form Event object data outgoing + let event_do: key_val = {}; if (!$idaa_slct.event_id) { event_do['account_id_random'] = $ae_loc.account_id; @@ -429,21 +433,23 @@ async function handle_submit_form(event: any) { console.log(event_do); if (!$idaa_slct.event_id) { - prom_api__event_obj = api.create_ae_obj_crud({ + prom_api__event_obj = events_func.create_ae_obj__event({ api_cfg: $ae_api, - obj_type: 'event', - fields: event_do, - key: $ae_api.api_crud_super_key, - log_lvl: 1 + account_id: $ae_loc.account_id, + data_kv: event_do, + log_lvl: log_lvl }) .then(function (event_obj_create_result) { if (!event_obj_create_result) { console.log('The result was null or false.'); return false; } - console.log(event_obj_create_result); + if (log_lvl) { + console.log('event_obj_create_result:', event_obj_create_result); + } $idaa_slct.event_id = event_obj_create_result.obj_id_random; + $idaa_slct.event_obj = event_obj_create_result; return event_obj_create_result; }) @@ -454,11 +460,14 @@ async function handle_submit_form(event: any) { }) .finally(() => { disable_submit_btn = false; + $idaa_sess.recovery_meetings.show__modal_edit = false; + $idaa_sess.recovery_meetings.show__modal_view = true; }); return prom_api__event_obj; } else { + prom_api__event_obj = events_func.update_ae_obj__event({ api_cfg: $ae_api, event_id: $idaa_slct.event_id, @@ -483,6 +492,7 @@ async function handle_submit_form(event: any) { }); return prom_api__event_obj; + } } @@ -509,6 +519,7 @@ async function handle_delete_event_obj( }) .then(function (event_obj_delete_result) { $idaa_sess.recovery_meetings.show__modal_edit = false; + $idaa_sess.recovery_meetings.show__modal_view = false; }) .catch(function (error) { console.log('The result was null or false when trying to delete.', error); diff --git a/src/routes/idaa/(idaa)/recovery_meetings/ae_idaa_comp__event_obj_id_view.svelte b/src/routes/idaa/(idaa)/recovery_meetings/ae_idaa_comp__event_obj_id_view.svelte index 903bdc63..ce89eba0 100644 --- a/src/routes/idaa/(idaa)/recovery_meetings/ae_idaa_comp__event_obj_id_view.svelte +++ b/src/routes/idaa/(idaa)/recovery_meetings/ae_idaa_comp__event_obj_id_view.svelte @@ -54,12 +54,12 @@ onDestroy(() => {
-
Description:
+
Description:
{@html $lq__event_obj?.description}
-
- Type of Recovery Meeting: +
+ Type of Recovery Meeting: {$lq__event_obj?.type}
@@ -67,11 +67,13 @@ onDestroy(() => {
+ Address: -
+ +
{#if $lq__event_obj?.location_address_json} {#if $lq__event_obj?.physical} @@ -112,9 +114,12 @@ onDestroy(() => {
{#if $lq__event_obj?.physical}
- Additional information: {@html $lq__event_obj?.location_text} + + Additional information: + + {@html $lq__event_obj?.location_text}
{/if}
@@ -122,10 +127,10 @@ onDestroy(() => {
@@ -146,7 +151,7 @@ onDestroy(() => {
@@ -164,9 +169,12 @@ onDestroy(() => {
- Additional information: {@html $lq__event_obj?.attend_text} + + Additional information: + + {@html $lq__event_obj?.attend_text}
@@ -219,7 +227,7 @@ onDestroy(() => { {ae_util.iso_datetime_formatter(`2024-01-01 ${$lq__event_obj?.recurring_start_time}`, 'time_12_short')} - End time: @@ -228,7 +236,7 @@ onDestroy(() => { - Timezone: @@ -263,7 +271,7 @@ onDestroy(() => { {#if $lq__event_obj?.contact_li_json && $lq__event_obj?.contact_li_json.length && $lq__event_obj?.contact_li_json[0].full_name}
Contact: @@ -294,7 +302,7 @@ onDestroy(() => { {#if $lq__event_obj?.contact_li_json && $lq__event_obj?.contact_li_json.length && $lq__event_obj?.contact_li_json[1].full_name}
Contact: @@ -328,7 +336,7 @@ onDestroy(() => {
+ class:hidden={!$ae_loc.administrator_access}> ID: {$lq__event_obj?.event_id_random} @@ -339,7 +347,7 @@ onDestroy(() => { Updated on: {ae_util.iso_datetime_formatter($lq__event_obj?.updated_on, 'datetime_12_short')} @@ -358,6 +366,9 @@ onDestroy(() => { .ae_value { font-weight: bold; } +.ae_value_semi { + font-weight: semi-bold; +} .event__user_timezone { font-size: smaller; diff --git a/src/routes/idaa/(idaa)/recovery_meetings/ae_idaa_comp__event_obj_li.svelte b/src/routes/idaa/(idaa)/recovery_meetings/ae_idaa_comp__event_obj_li.svelte index d2ba778a..10a9b2b0 100644 --- a/src/routes/idaa/(idaa)/recovery_meetings/ae_idaa_comp__event_obj_li.svelte +++ b/src/routes/idaa/(idaa)/recovery_meetings/ae_idaa_comp__event_obj_li.svelte @@ -50,7 +50,9 @@ onMount(() => {

- {idaa_event_obj?.name} + + + {idaa_event_obj?.name} {#if idaa_event_obj?.physical && idaa_event_obj?.virtual} F2F and Virtual diff --git a/src/routes/idaa/(idaa)/recovery_meetings/ae_idaa_comp__event_obj_qry.svelte b/src/routes/idaa/(idaa)/recovery_meetings/ae_idaa_comp__event_obj_qry.svelte index 3309f24e..006c480f 100644 --- a/src/routes/idaa/(idaa)/recovery_meetings/ae_idaa_comp__event_obj_qry.svelte +++ b/src/routes/idaa/(idaa)/recovery_meetings/ae_idaa_comp__event_obj_qry.svelte @@ -519,7 +519,7 @@ if ($idaa_loc.recovery_meetings.qry__fulltext_str && $idaa_loc.recovery_meetings $idaa_sess.recovery_meetings.show__modal_edit = true; }} class="btn_new_recovery_meeting btn btn-sm variant-ghost-warning hover:variant-filled-warning transition text-xs" - disabled={!$ae_loc.administrator_access} + disabled={!$ae_loc.authenticated_access} > Create New Meeting