Plugin Directory

Changeset 3769397


Ignore:
Timestamp:
10/27/2026 08:05:02 PM (4 days ago)
Author:
joinery
Message:

Update to version 0.4.0 from GitHub

Location:
joinery-core
Files:
4 added
22 edited
1 copied

Legend:

Unmodified
Added
Removed
  • joinery-core/assets/screenshot-1.png

    • Property svn:mime-type changed from application/octet-stream to image/png
  • joinery-core/assets/screenshot-2.png

    • Property svn:mime-type changed from application/octet-stream to image/png
  • joinery-core/assets/screenshot-3.png

    • Property svn:mime-type changed from application/octet-stream to image/png
  • joinery-core/assets/screenshot-4.png

    • Property svn:mime-type changed from application/octet-stream to image/png
  • joinery-core/tags/0.4.0/assets/packages/rules-ui/src/index.js

    r3767875 r3769397  
    1717import { Card, PageIntro } from '@joinery/admin-ui';
    1818import { FieldControl } from './fields';
     19import { MoreRuleTypes } from './more';
    1920import './rules.css';
    2021
    … …  
    7172                        />
    7273                        <div className="joinery-rules">
    73                                 <Card className="joinery-rules__list">
    74                                         <p className="joinery-eyebrow">
    75                                                 { __( 'All rules', 'joinery-core' ) } ({ rules.length })
    76                                         </p>
    77                                         { rules.length === 0 && <p>{ __( 'No rules yet. Add your first one.', 'joinery-core' ) }</p> }
    78                                         { rules.map( ( rule ) => (
    79                                                 <button
    80                                                         key={ rule.id }
    81                                                         type="button"
    82                                                         className="joinery-rules__item"
    83                                                         aria-current={ editing?.id === rule.id ? 'true' : undefined }
    84                                                         onClick={ () => setEditing( rule ) }
    85                                                 >
    86                                                         <strong>{ rule.name }</strong>
    87                                                         <span>{ rule.active ? __( 'Active', 'joinery-core' ) : __( 'Off', 'joinery-core' ) }</span>
    88                                                 </button>
    89                                         ) ) }
    90                                 </Card>
     74                                <div className="joinery-rules__side">
     75                                        <Card className="joinery-rules__list">
     76                                                <p className="joinery-eyebrow">
     77                                                        { __( 'All rules', 'joinery-core' ) } ({ rules.length })
     78                                                </p>
     79                                                { rules.length === 0 && <p>{ __( 'No rules yet. Add your first one.', 'joinery-core' ) }</p> }
     80                                                { rules.map( ( rule ) => (
     81                                                        <button
     82                                                                key={ rule.id }
     83                                                                type="button"
     84                                                                className="joinery-rules__item"
     85                                                                aria-current={ editing?.id === rule.id ? 'true' : undefined }
     86                                                                onClick={ () => setEditing( rule ) }
     87                                                        >
     88                                                                <strong>{ rule.name }</strong>
     89                                                                <span>{ rule.active ? __( 'Active', 'joinery-core' ) : __( 'Off', 'joinery-core' ) }</span>
     90                                                        </button>
     91                                                ) ) }
     92                                        </Card>
     93                                        <MoreRuleTypes schema={ schema } />
     94                                </div>
    9195                                { editing && (
    9296                                        <RuleEditor
  • joinery-core/tags/0.4.0/assets/packages/rules-ui/src/rules.css

    r3767875 r3769397  
    66}
    77
    8 .joinery-rules__list {
     8.joinery-rules__side {
    99  flex: 1 1 280px;
    1010  max-width: 360px;
     11  display: flex;
     12  flex-direction: column;
     13  gap: 20px;
     14}
     15
     16.joinery-rules__list {
    1117  display: flex;
    1218  flex-direction: column;
    … …  
    3642.joinery-rules__item[aria-current="true"] {
    3743  background: var(--joinery-field);
     44}
     45
     46.joinery-more {
     47  display: flex;
     48  flex-direction: column;
     49  align-items: flex-start;
     50  gap: 10px;
     51  color: var(--joinery-ink-2);
     52}
     53
     54.joinery-more p,
     55.joinery-more ul {
     56  margin: 0;
     57}
     58
     59.joinery-more ul {
     60  display: flex;
     61  flex-direction: column;
     62  gap: 12px;
     63}
     64
     65.joinery-more li {
     66  display: flex;
     67  flex-direction: column;
     68  gap: 2px;
     69  margin: 0;
     70}
     71
     72.joinery-more li span {
     73  color: var(--joinery-muted);
     74  font-size: 13px;
    3875}
    3976
  • joinery-core/tags/0.4.0/build/admin-rtl.css

    r3767875 r3769397  
    1 .joinery-rules{align-items:flex-start;display:flex;flex-wrap:wrap;gap:20px}.joinery-rules__list{display:flex;flex:1 1 280px;flex-direction:column;gap:8px;max-width:360px}.joinery-rules__item{background:transparent;border:0;border-radius:12px;color:var(--joinery-ink);cursor:pointer;display:flex;font:inherit;gap:12px;justify-content:space-between;padding:12px 14px;text-align:right;width:100%}.joinery-rules__item span{color:var(--joinery-muted);font-size:12px}.joinery-rules__item[aria-current=true]{background:var(--joinery-field)}.joinery-editor{display:flex;flex:999 1 520px;flex-direction:column;gap:22px;min-width:0}.joinery-editor__foot,.joinery-editor__head{align-items:flex-end;display:flex;flex-wrap:wrap;gap:16px;justify-content:space-between}.joinery-editor__foot{border-top:1px solid var(--joinery-line);padding-top:18px}.joinery-parts{flex-direction:column;gap:10px}.joinery-part,.joinery-parts{align-items:flex-start;display:flex}.joinery-part{background:var(--joinery-field);border-radius:14px;box-sizing:border-box;flex-wrap:wrap;gap:12px;padding:14px;width:100%}.joinery-part>.components-button.is-link{margin-top:23px;min-height:40px}.joinery-tiers{border:0;display:flex;flex-direction:column;gap:8px;margin:0;padding:0}.joinery-tiers__row{align-items:flex-end;display:flex;gap:10px}.joinery-editor__head>.components-base-control{flex:1 1 320px;max-width:520px}
     1.joinery-rules{align-items:flex-start;display:flex;flex-wrap:wrap;gap:20px}.joinery-rules__side{display:flex;flex:1 1 280px;flex-direction:column;gap:20px;max-width:360px}.joinery-rules__list{display:flex;flex-direction:column;gap:8px}.joinery-rules__item{background:transparent;border:0;border-radius:12px;color:var(--joinery-ink);cursor:pointer;display:flex;font:inherit;gap:12px;justify-content:space-between;padding:12px 14px;text-align:right;width:100%}.joinery-rules__item span{color:var(--joinery-muted);font-size:12px}.joinery-rules__item[aria-current=true]{background:var(--joinery-field)}.joinery-more{align-items:flex-start;color:var(--joinery-ink-2);display:flex;flex-direction:column;gap:10px}.joinery-more p,.joinery-more ul{margin:0}.joinery-more ul{gap:12px}.joinery-more li,.joinery-more ul{display:flex;flex-direction:column}.joinery-more li{gap:2px;margin:0}.joinery-more li span{color:var(--joinery-muted);font-size:13px}.joinery-editor{display:flex;flex:999 1 520px;flex-direction:column;gap:22px;min-width:0}.joinery-editor__foot,.joinery-editor__head{align-items:flex-end;display:flex;flex-wrap:wrap;gap:16px;justify-content:space-between}.joinery-editor__foot{border-top:1px solid var(--joinery-line);padding-top:18px}.joinery-parts{flex-direction:column;gap:10px}.joinery-part,.joinery-parts{align-items:flex-start;display:flex}.joinery-part{background:var(--joinery-field);border-radius:14px;box-sizing:border-box;flex-wrap:wrap;gap:12px;padding:14px;width:100%}.joinery-part>.components-button.is-link{margin-top:23px;min-height:40px}.joinery-tiers{border:0;display:flex;flex-direction:column;gap:8px;margin:0;padding:0}.joinery-tiers__row{align-items:flex-end;display:flex;gap:10px}.joinery-editor__head>.components-base-control{flex:1 1 320px;max-width:520px}
    22.joinery-app{--joinery-ink:#1b1e16;--joinery-ink-2:#545a4a;--joinery-muted:#6e7462;--joinery-line:#ecebe0;--joinery-field:#f5f4ec;--joinery-surface:#fff;--joinery-forest:#1f2419;--joinery-clay:#a85a33;--joinery-gradient:linear-gradient(115deg,#efe8d2 25%,#b8be8a 62%,#7f8a52);--joinery-sans:"Geist",system-ui,-apple-system,"Segoe UI",sans-serif;--joinery-mono:"Geist Mono",ui-monospace,Menlo,monospace;color:var(--joinery-ink);display:flex;flex-direction:column;font-family:var(--joinery-sans);font-size:14px;gap:20px;margin-top:16px}.joinery-card{background:var(--joinery-surface);border-radius:20px;box-shadow:0 0 0 1px rgba(27,30,22,.06),0 1px 3px rgba(27,30,22,.06);padding:20px 24px}.joinery-header{flex-wrap:wrap;gap:16px;justify-content:space-between;padding:14px 20px}.joinery-brand,.joinery-header{align-items:center;display:flex}.joinery-brand{font-size:19px;font-weight:600;gap:10px;letter-spacing:-.02em}.joinery-tabs{background:var(--joinery-line);border-radius:999px;display:flex;flex-wrap:wrap;gap:4px;padding:4px}.joinery-tab{background:transparent;border:0;border-radius:999px;color:var(--joinery-ink-2);cursor:pointer;font:500 14px var(--joinery-sans);padding:8px 16px}.joinery-tab[aria-current=page]{background:var(--joinery-surface);box-shadow:0 0 0 1px rgba(27,30,22,.08),0 1px 2px rgba(27,30,22,.1);color:var(--joinery-ink)}.joinery-intro{align-items:flex-end;background:var(--joinery-gradient);border-radius:20px;display:flex;flex-wrap:wrap;gap:16px;justify-content:space-between;padding:28px}.joinery-intro h1{color:var(--joinery-ink);font:500 36px/1.05 var(--joinery-sans);letter-spacing:-.04em;margin:4px 0;padding:0}.joinery-intro__text{color:rgba(27,30,22,.75);font-size:15px;font-weight:500;margin:0}.joinery-eyebrow{color:var(--joinery-muted);font:500 12px var(--joinery-mono);letter-spacing:.12em;margin:0;text-transform:uppercase}.joinery-intro .joinery-eyebrow{color:rgba(27,30,22,.7)}.joinery-tag{border-radius:6px;display:inline-block;font-size:12px;font-weight:500;padding:2px 8px}.joinery-tag--pricing{background:#e5ead0;color:#4b5b1f}.joinery-tag--b2b{background:#dde8e5;color:#2e5650}.joinery-tag--wallet{background:#f5d9c8;color:#8c3a1a}.joinery-tag--options{background:#f4e3bc;color:#7a5410}.joinery-tag--neutral{background:var(--joinery-line);color:var(--joinery-ink-2)}.joinery-app .components-button.is-primary{background:var(--joinery-ink);border-radius:999px;min-height:40px;padding:0 20px}.joinery-app .components-button.is-primary:hover:not(:disabled){background:#2d3425}.joinery-app .components-button.is-secondary{border-radius:999px;box-shadow:inset 0 0 0 1px rgba(27,30,22,.2);color:var(--joinery-ink);min-height:40px}.joinery-app .components-base-control__field input,.joinery-app .components-base-control__field select,.joinery-app .components-select-control__input,.joinery-app .components-text-control__input{border-radius:999px}
  • joinery-core/tags/0.4.0/build/admin.asset.php

    r3767875 r3769397  
    77                'wp-i18n'
    88        ),
    9         'version' => '2ec3f350f6eb4ce8ba7f'
     9        'version' => '27bfce624f7c742c41be'
    1010);
  • joinery-core/tags/0.4.0/build/admin.css

    r3767875 r3769397  
    1 .joinery-rules{align-items:flex-start;display:flex;flex-wrap:wrap;gap:20px}.joinery-rules__list{display:flex;flex:1 1 280px;flex-direction:column;gap:8px;max-width:360px}.joinery-rules__item{background:transparent;border:0;border-radius:12px;color:var(--joinery-ink);cursor:pointer;display:flex;font:inherit;gap:12px;justify-content:space-between;padding:12px 14px;text-align:left;width:100%}.joinery-rules__item span{color:var(--joinery-muted);font-size:12px}.joinery-rules__item[aria-current=true]{background:var(--joinery-field)}.joinery-editor{display:flex;flex:999 1 520px;flex-direction:column;gap:22px;min-width:0}.joinery-editor__foot,.joinery-editor__head{align-items:flex-end;display:flex;flex-wrap:wrap;gap:16px;justify-content:space-between}.joinery-editor__foot{border-top:1px solid var(--joinery-line);padding-top:18px}.joinery-parts{flex-direction:column;gap:10px}.joinery-part,.joinery-parts{align-items:flex-start;display:flex}.joinery-part{background:var(--joinery-field);border-radius:14px;box-sizing:border-box;flex-wrap:wrap;gap:12px;padding:14px;width:100%}.joinery-part>.components-button.is-link{margin-top:23px;min-height:40px}.joinery-tiers{border:0;display:flex;flex-direction:column;gap:8px;margin:0;padding:0}.joinery-tiers__row{align-items:flex-end;display:flex;gap:10px}.joinery-editor__head>.components-base-control{flex:1 1 320px;max-width:520px}
     1.joinery-rules{align-items:flex-start;display:flex;flex-wrap:wrap;gap:20px}.joinery-rules__side{display:flex;flex:1 1 280px;flex-direction:column;gap:20px;max-width:360px}.joinery-rules__list{display:flex;flex-direction:column;gap:8px}.joinery-rules__item{background:transparent;border:0;border-radius:12px;color:var(--joinery-ink);cursor:pointer;display:flex;font:inherit;gap:12px;justify-content:space-between;padding:12px 14px;text-align:left;width:100%}.joinery-rules__item span{color:var(--joinery-muted);font-size:12px}.joinery-rules__item[aria-current=true]{background:var(--joinery-field)}.joinery-more{align-items:flex-start;color:var(--joinery-ink-2);display:flex;flex-direction:column;gap:10px}.joinery-more p,.joinery-more ul{margin:0}.joinery-more ul{gap:12px}.joinery-more li,.joinery-more ul{display:flex;flex-direction:column}.joinery-more li{gap:2px;margin:0}.joinery-more li span{color:var(--joinery-muted);font-size:13px}.joinery-editor{display:flex;flex:999 1 520px;flex-direction:column;gap:22px;min-width:0}.joinery-editor__foot,.joinery-editor__head{align-items:flex-end;display:flex;flex-wrap:wrap;gap:16px;justify-content:space-between}.joinery-editor__foot{border-top:1px solid var(--joinery-line);padding-top:18px}.joinery-parts{flex-direction:column;gap:10px}.joinery-part,.joinery-parts{align-items:flex-start;display:flex}.joinery-part{background:var(--joinery-field);border-radius:14px;box-sizing:border-box;flex-wrap:wrap;gap:12px;padding:14px;width:100%}.joinery-part>.components-button.is-link{margin-top:23px;min-height:40px}.joinery-tiers{border:0;display:flex;flex-direction:column;gap:8px;margin:0;padding:0}.joinery-tiers__row{align-items:flex-end;display:flex;gap:10px}.joinery-editor__head>.components-base-control{flex:1 1 320px;max-width:520px}
    22.joinery-app{--joinery-ink:#1b1e16;--joinery-ink-2:#545a4a;--joinery-muted:#6e7462;--joinery-line:#ecebe0;--joinery-field:#f5f4ec;--joinery-surface:#fff;--joinery-forest:#1f2419;--joinery-clay:#a85a33;--joinery-gradient:linear-gradient(115deg,#efe8d2 25%,#b8be8a 62%,#7f8a52);--joinery-sans:"Geist",system-ui,-apple-system,"Segoe UI",sans-serif;--joinery-mono:"Geist Mono",ui-monospace,Menlo,monospace;color:var(--joinery-ink);display:flex;flex-direction:column;font-family:var(--joinery-sans);font-size:14px;gap:20px;margin-top:16px}.joinery-card{background:var(--joinery-surface);border-radius:20px;box-shadow:0 0 0 1px rgba(27,30,22,.06),0 1px 3px rgba(27,30,22,.06);padding:20px 24px}.joinery-header{flex-wrap:wrap;gap:16px;justify-content:space-between;padding:14px 20px}.joinery-brand,.joinery-header{align-items:center;display:flex}.joinery-brand{font-size:19px;font-weight:600;gap:10px;letter-spacing:-.02em}.joinery-tabs{background:var(--joinery-line);border-radius:999px;display:flex;flex-wrap:wrap;gap:4px;padding:4px}.joinery-tab{background:transparent;border:0;border-radius:999px;color:var(--joinery-ink-2);cursor:pointer;font:500 14px var(--joinery-sans);padding:8px 16px}.joinery-tab[aria-current=page]{background:var(--joinery-surface);box-shadow:0 0 0 1px rgba(27,30,22,.08),0 1px 2px rgba(27,30,22,.1);color:var(--joinery-ink)}.joinery-intro{align-items:flex-end;background:var(--joinery-gradient);border-radius:20px;display:flex;flex-wrap:wrap;gap:16px;justify-content:space-between;padding:28px}.joinery-intro h1{color:var(--joinery-ink);font:500 36px/1.05 var(--joinery-sans);letter-spacing:-.04em;margin:4px 0;padding:0}.joinery-intro__text{color:rgba(27,30,22,.75);font-size:15px;font-weight:500;margin:0}.joinery-eyebrow{color:var(--joinery-muted);font:500 12px var(--joinery-mono);letter-spacing:.12em;margin:0;text-transform:uppercase}.joinery-intro .joinery-eyebrow{color:rgba(27,30,22,.7)}.joinery-tag{border-radius:6px;display:inline-block;font-size:12px;font-weight:500;padding:2px 8px}.joinery-tag--pricing{background:#e5ead0;color:#4b5b1f}.joinery-tag--b2b{background:#dde8e5;color:#2e5650}.joinery-tag--wallet{background:#f5d9c8;color:#8c3a1a}.joinery-tag--options{background:#f4e3bc;color:#7a5410}.joinery-tag--neutral{background:var(--joinery-line);color:var(--joinery-ink-2)}.joinery-app .components-button.is-primary{background:var(--joinery-ink);border-radius:999px;min-height:40px;padding:0 20px}.joinery-app .components-button.is-primary:hover:not(:disabled){background:#2d3425}.joinery-app .components-button.is-secondary{border-radius:999px;box-shadow:inset 0 0 0 1px rgba(27,30,22,.2);color:var(--joinery-ink);min-height:40px}.joinery-app .components-base-control__field input,.joinery-app .components-base-control__field select,.joinery-app .components-select-control__input,.joinery-app .components-text-control__input{border-radius:999px}
  • joinery-core/tags/0.4.0/build/admin.js

    r3767875 r3769397  
    1 (()=>{"use strict";const e={n:n=>{const t=n&&n.__esModule?()=>n.default:()=>n;return e.d(t,{a:t}),t},d:(n,t)=>{for(var r in t)e.o(t,r)&&!e.o(n,r)&&Object.defineProperty(n,r,{enumerable:!0,get:t[r]})},o:(e,n)=>Object.hasOwn(e,n)},n=window.wp.element,t=window.wp.i18n,r=window.ReactJSXRuntime;function o(){return(0,r.jsxs)("svg",{width:"26",height:"26",viewBox:"0 0 28 28","aria-hidden":"true",children:[(0,r.jsx)("rect",{x:"2",y:"3",width:"24",height:"9",rx:"2",fill:"currentColor"}),(0,r.jsx)("path",{d:"M9 12 L5 25 H23 L19 12 Z",fill:"currentColor",fillOpacity:"0.55"})]})}function a({tabs:e}){const a=()=>window.location.hash.replace(/^#/,""),[i,s]=(0,n.useState)(()=>a()||e[0]?.id),l=e.find(e=>e.id===i)||e[0];return(0,n.useEffect)(()=>{const n=()=>s(a()||e[0]?.id);return window.addEventListener("hashchange",n),()=>window.removeEventListener("hashchange",n)},[e]),(0,r.jsxs)("div",{className:"joinery-app",children:[(0,r.jsxs)("header",{className:"joinery-card joinery-header",children:[(0,r.jsxs)("div",{className:"joinery-brand",children:[(0,r.jsx)(o,{}),(0,r.jsx)("span",{children:(0,t.__)("Joinery","joinery-core")})]}),(0,r.jsx)("nav",{className:"joinery-tabs","aria-label":(0,t.__)("Joinery sections","joinery-core"),children:e.map(e=>(0,r.jsx)("button",{type:"button",className:"joinery-tab","aria-current":e.id===l?.id?"page":void 0,onClick:()=>{return n=e.id,s(n),void window.history.replaceState(null,"",`#${n}`);var n},children:e.label},e.id))})]}),l?.render()]})}function i({eyebrow:e,title:n,description:t,action:o}){return(0,r.jsxs)("section",{className:"joinery-intro",children:[(0,r.jsxs)("div",{children:[(0,r.jsx)("p",{className:"joinery-eyebrow",children:e}),(0,r.jsx)("h1",{children:n}),t&&(0,r.jsx)("p",{className:"joinery-intro__text",children:t})]}),o]})}function s({className:e="",children:n,...t}){return(0,r.jsx)("section",{className:`joinery-card ${e}`,...t,children:n})}const l=window.wp.apiFetch;var c=e.n(l);const d=window.wp.components;function u({field:e,value:n,onChange:o}){const a=(e,t)=>o(n.map((n,r)=>r===e?{...n,...t}:n));return(0,r.jsxs)("fieldset",{className:"joinery-tiers",children:[(0,r.jsx)("legend",{children:e.label}),n.map((e,i)=>(0,r.jsxs)("div",{className:"joinery-tiers__row",children:[(0,r.jsx)(d.TextControl,{__nextHasNoMarginBottom:!0,type:"number",label:(0,t.__)("From quantity","joinery-core"),value:e.min??"",onChange:e=>a(i,{min:Number(e)})}),(0,r.jsx)(d.TextControl,{__nextHasNoMarginBottom:!0,type:"number",label:(0,t.__)("% off","joinery-core"),value:e.percent??"",onChange:e=>a(i,{percent:Number(e)})}),(0,r.jsx)(d.Button,{variant:"link",isDestructive:!0,onClick:()=>o(n.filter((e,n)=>n!==i)),children:(0,t.__)("Remove","joinery-core")})]},i)),(0,r.jsx)(d.Button,{variant:"secondary",onClick:()=>o([...n,{min:"",percent:""}]),children:(0,t.__)("+ Add tier","joinery-core")})]})}function h({field:e,schema:n,value:t,onChange:o}){switch(e.type){case"number":case"money":case"percent":return(0,r.jsx)(d.TextControl,{__nextHasNoMarginBottom:!0,type:"number",label:"money"===e.type?`${e.label} (${n.currency})`:"percent"===e.type?`${e.label} (%)`:e.label,help:e.help,min:e.min,max:e.max,value:t??"",onChange:e=>o(""===e?"":Number(e))});case"select":return(0,r.jsx)(d.SelectControl,{__nextHasNoMarginBottom:!0,label:e.label,help:e.help,value:t??Object.keys(e.options||{})[0],options:Object.entries(e.options||{}).map(([e,n])=>({value:e,label:n})),onChange:o});case"role-select":return(0,r.jsx)(j,{label:e.label,choices:n.roles,value:t||[],onChange:o});case"category-select":return(0,r.jsx)(m,{label:e.label,value:t||[],onChange:o});case"product-select":return(0,r.jsx)(y,{label:e.label,help:e.help,value:t,onChange:o});case"datetime":return(0,r.jsx)(d.TextControl,{__nextHasNoMarginBottom:!0,type:"datetime-local",label:e.label,value:t||"",onChange:o});case"tiers":return(0,r.jsx)(u,{field:e,value:t||[],onChange:o});default:return(0,r.jsx)(d.TextControl,{__nextHasNoMarginBottom:!0,label:e.label,value:t??"",onChange:o})}}function j({label:e,choices:n,value:t,onChange:o}){const a=Object.fromEntries(Object.entries(n).map(([e,n])=>[n,e]));return(0,r.jsx)(d.FormTokenField,{__nextHasNoMarginBottom:!0,__experimentalExpandOnFocus:!0,label:e,value:t.map(e=>n[e]||e),suggestions:Object.values(n),onChange:e=>o(e.map(e=>a[e]).filter(Boolean))})}function m({label:e,value:o,onChange:a}){const[i,s]=(0,n.useState)({});return(0,n.useEffect)(()=>{c()({path:"/wp/v2/product_cat?per_page=100&_fields=id,name"}).then(e=>s(Object.fromEntries(e.map(e=>[e.id,e.name])))).catch(()=>s({}))},[]),(0,r.jsx)(j,{label:e||(0,t.__)("Categories","joinery-core"),choices:i,value:o.map(String),onChange:e=>a(e.map(Number))})}function y({label:e,help:t,value:o,onChange:a}){const[i,s]=(0,n.useState)([]),l=e=>e.map(e=>({value:String(e.id),label:e.name})),u=e=>c()({path:`/wc/v3/products?status=publish&type=simple&per_page=20&_fields=id,name&search=${encodeURIComponent(e||"")}`}).then(e=>s(n=>[...n.filter(e=>e.value===String(o)),...l(e).filter(e=>e.value!==String(o))])).catch(()=>{});return(0,n.useEffect)(()=>{o&&c()({path:`/wc/v3/products/${Number(o)}?_fields=id,name`}).then(e=>s(n=>[...l([e]),...n])).catch(()=>{}),u("")},[]),(0,r.jsx)(d.ComboboxControl,{__nextHasNoMarginBottom:!0,label:e,help:t,value:o?String(o):null,options:i,onFilterValueChange:u,onChange:e=>a(e?Number(e):"")})}function p(){const[e,o]=(0,n.useState)(null),[a,l]=(0,n.useState)([]),[u,h]=(0,n.useState)(null),[j,m]=(0,n.useState)(""),y=()=>Promise.all([c()({path:"/joinery/v1/schema"}),c()({path:"/joinery/v1/rules"})]).then(([e,n])=>{o(e),l(n),h(e=>e||n[0]||{id:0,name:"",active:!0,priority:10,exclusive:!1,conditions:[],actions:[]})}).catch(e=>m(e.message));return(0,n.useEffect)(()=>{y()},[]),j?(0,r.jsx)(d.Notice,{status:"error",isDismissible:!1,children:j}):e?(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(i,{eyebrow:(0,t.__)("Rules","joinery-core"),title:(0,t.__)("Every price, discount and reward rule.","joinery-core"),description:(0,t.__)("Rules here are shared by all your Joinery plugins.","joinery-core"),action:(0,r.jsx)(d.Button,{variant:"primary",onClick:()=>h({id:0,name:"",active:!0,priority:10,exclusive:!1,conditions:[],actions:[]}),children:(0,t.__)("Add rule","joinery-core")})}),(0,r.jsxs)("div",{className:"joinery-rules",children:[(0,r.jsxs)(s,{className:"joinery-rules__list",children:[(0,r.jsxs)("p",{className:"joinery-eyebrow",children:[(0,t.__)("All rules","joinery-core")," (",a.length,")"]}),0===a.length&&(0,r.jsx)("p",{children:(0,t.__)("No rules yet. Add your first one.","joinery-core")}),a.map(e=>(0,r.jsxs)("button",{type:"button",className:"joinery-rules__item","aria-current":u?.id===e.id?"true":void 0,onClick:()=>h(e),children:[(0,r.jsx)("strong",{children:e.name}),(0,r.jsx)("span",{children:e.active?(0,t.__)("Active","joinery-core"):(0,t.__)("Off","joinery-core")})]},e.id))]}),u&&(0,r.jsx)(x,{rule:u,schema:e,onSaved:e=>{h(e),y()}},u.id||"new")]})]}):(0,r.jsx)(d.Spinner,{})}function x({rule:e,schema:o,onSaved:a}){const[i,l]=(0,n.useState)(e),[u,h]=(0,n.useState)(!1),[j,m]=(0,n.useState)(null),y=e=>l(n=>({...n,...e}));return(0,r.jsxs)(s,{className:"joinery-editor",children:[(0,r.jsxs)("div",{className:"joinery-editor__head",children:[(0,r.jsx)(d.TextControl,{__nextHasNoMarginBottom:!0,label:(0,t.__)("Rule name","joinery-core"),value:i.name,onChange:e=>y({name:e})}),(0,r.jsx)(d.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,t.__)("Rule is active","joinery-core"),checked:i.active,onChange:e=>y({active:e})})]}),(0,r.jsx)(_,{title:(0,t.__)("When all of these are true","joinery-core"),addLabel:(0,t.__)("+ Add condition","joinery-core"),parts:i.conditions,available:o.conditions,schema:o,onChange:e=>y({conditions:e})}),(0,r.jsx)(_,{title:(0,t.__)("Then do this","joinery-core"),addLabel:(0,t.__)("+ Add action","joinery-core"),parts:i.actions,available:o.actions,schema:o,onChange:e=>y({actions:e})}),(0,r.jsxs)("div",{className:"joinery-editor__foot",children:[(0,r.jsx)(d.TextControl,{__nextHasNoMarginBottom:!0,type:"number",label:(0,t.__)("Priority (lower runs first)","joinery-core"),value:String(i.priority),onChange:e=>y({priority:Number(e)||0})}),(0,r.jsx)(d.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,t.__)("Don't combine with other rules","joinery-core"),checked:i.exclusive,onChange:e=>y({exclusive:e})}),(0,r.jsx)(d.Button,{variant:"primary",onClick:()=>{h(!0),m(null),c()({path:i.id?`/joinery/v1/rules/${i.id}`:"/joinery/v1/rules",method:i.id?"PUT":"POST",data:i}).then(e=>{m({status:"success",text:(0,t.__)("Rule saved.","joinery-core")}),a(e)}).catch(e=>m({status:"error",text:e.message})).finally(()=>h(!1))},isBusy:u,disabled:u,children:(0,t.__)("Save rule","joinery-core")})]}),j&&(0,r.jsx)(d.Notice,{status:j.status,onRemove:()=>m(null),children:j.text})]})}function _({title:e,addLabel:n,parts:o,available:a,schema:i,onChange:s}){const l=(e,n)=>s(o.map((t,r)=>r===e?n:t));return(0,r.jsxs)("div",{className:"joinery-parts",children:[(0,r.jsx)("p",{className:"joinery-eyebrow",children:e}),o.map((e,n)=>{const c=a.find(n=>n.id===e.type);return(0,r.jsxs)("div",{className:"joinery-part",children:[(0,r.jsx)(d.SelectControl,{__nextHasNoMarginBottom:!0,label:(0,t.__)("Type","joinery-core"),value:e.type,options:a.map(e=>({value:e.id,label:e.label})),onChange:e=>l(n,{type:e,settings:{}})}),c?.fields.map(t=>(0,r.jsx)(h,{field:t,schema:i,value:e.settings[t.key]??t.default,onChange:r=>l(n,{...e,settings:{...e.settings,[t.key]:r}})},t.key)),(0,r.jsx)(d.Button,{variant:"link",isDestructive:!0,onClick:()=>(e=>s(o.filter((n,t)=>t!==e)))(n),children:(0,t.__)("Remove","joinery-core")})]},n)}),(0,r.jsx)(d.Button,{variant:"secondary",onClick:()=>a[0]&&s([...o,{type:a[0].id,settings:{}}]),children:n})]})}const v=[{id:"rules",label:(0,t.__)("Rules","joinery-core"),render:()=>(0,r.jsx)(p,{})}];window.joinery=window.joinery||{},window.joinery.registerTab=e=>v.push(e),window.joinery.ui={Card:s,PageIntro:i,Tag:function({tone:e="neutral",children:n}){return(0,r.jsx)("span",{className:`joinery-tag joinery-tag--${e}`,children:n})}},document.addEventListener("DOMContentLoaded",()=>{const e=document.getElementById("joinery-admin-root");e&&(0,n.createRoot)(e).render((0,r.jsx)(a,{tabs:v}))})})();
     1(()=>{"use strict";const e={n:n=>{const r=n&&n.__esModule?()=>n.default:()=>n;return e.d(r,{a:r}),r},d:(n,r)=>{for(var t in r)e.o(r,t)&&!e.o(n,t)&&Object.defineProperty(n,t,{enumerable:!0,get:r[t]})},o:(e,n)=>Object.hasOwn(e,n)},n=window.wp.element,r=window.wp.i18n,t=window.ReactJSXRuntime;function i(){return(0,t.jsxs)("svg",{width:"26",height:"26",viewBox:"0 0 28 28","aria-hidden":"true",children:[(0,t.jsx)("rect",{x:"2",y:"3",width:"24",height:"9",rx:"2",fill:"currentColor"}),(0,t.jsx)("path",{d:"M9 12 L5 25 H23 L19 12 Z",fill:"currentColor",fillOpacity:"0.55"})]})}function o({tabs:e}){const o=()=>window.location.hash.replace(/^#/,""),[a,s]=(0,n.useState)(()=>o()||e[0]?.id),l=e.find(e=>e.id===a)||e[0];return(0,n.useEffect)(()=>{const n=()=>s(o()||e[0]?.id);return window.addEventListener("hashchange",n),()=>window.removeEventListener("hashchange",n)},[e]),(0,t.jsxs)("div",{className:"joinery-app",children:[(0,t.jsxs)("header",{className:"joinery-card joinery-header",children:[(0,t.jsxs)("div",{className:"joinery-brand",children:[(0,t.jsx)(i,{}),(0,t.jsx)("span",{children:(0,r.__)("Joinery","joinery-core")})]}),(0,t.jsx)("nav",{className:"joinery-tabs","aria-label":(0,r.__)("Joinery sections","joinery-core"),children:e.map(e=>(0,t.jsx)("button",{type:"button",className:"joinery-tab","aria-current":e.id===l?.id?"page":void 0,onClick:()=>{return n=e.id,s(n),void window.history.replaceState(null,"",`#${n}`);var n},children:e.label},e.id))})]}),l?.render()]})}function a({eyebrow:e,title:n,description:r,action:i}){return(0,t.jsxs)("section",{className:"joinery-intro",children:[(0,t.jsxs)("div",{children:[(0,t.jsx)("p",{className:"joinery-eyebrow",children:e}),(0,t.jsx)("h1",{children:n}),r&&(0,t.jsx)("p",{className:"joinery-intro__text",children:r})]}),i]})}function s({className:e="",children:n,...r}){return(0,t.jsx)("section",{className:`joinery-card ${e}`,...r,children:n})}const l=window.wp.apiFetch;var c=e.n(l);const d=window.wp.components;function u({field:e,value:n,onChange:i}){const o=(e,r)=>i(n.map((n,t)=>t===e?{...n,...r}:n));return(0,t.jsxs)("fieldset",{className:"joinery-tiers",children:[(0,t.jsx)("legend",{children:e.label}),n.map((e,a)=>(0,t.jsxs)("div",{className:"joinery-tiers__row",children:[(0,t.jsx)(d.TextControl,{__nextHasNoMarginBottom:!0,type:"number",label:(0,r.__)("From quantity","joinery-core"),value:e.min??"",onChange:e=>o(a,{min:Number(e)})}),(0,t.jsx)(d.TextControl,{__nextHasNoMarginBottom:!0,type:"number",label:(0,r.__)("% off","joinery-core"),value:e.percent??"",onChange:e=>o(a,{percent:Number(e)})}),(0,t.jsx)(d.Button,{variant:"link",isDestructive:!0,onClick:()=>i(n.filter((e,n)=>n!==a)),children:(0,r.__)("Remove","joinery-core")})]},a)),(0,t.jsx)(d.Button,{variant:"secondary",onClick:()=>i([...n,{min:"",percent:""}]),children:(0,r.__)("+ Add tier","joinery-core")})]})}function h({field:e,schema:n,value:r,onChange:i}){switch(e.type){case"number":case"money":case"percent":return(0,t.jsx)(d.TextControl,{__nextHasNoMarginBottom:!0,type:"number",label:"money"===e.type?`${e.label} (${n.currency})`:"percent"===e.type?`${e.label} (%)`:e.label,help:e.help,min:e.min,max:e.max,value:r??"",onChange:e=>i(""===e?"":Number(e))});case"select":return(0,t.jsx)(d.SelectControl,{__nextHasNoMarginBottom:!0,label:e.label,help:e.help,value:r??Object.keys(e.options||{})[0],options:Object.entries(e.options||{}).map(([e,n])=>({value:e,label:n})),onChange:i});case"role-select":return(0,t.jsx)(j,{label:e.label,choices:n.roles,value:r||[],onChange:i});case"category-select":return(0,t.jsx)(m,{label:e.label,value:r||[],onChange:i});case"product-select":return(0,t.jsx)(y,{label:e.label,help:e.help,value:r,onChange:i});case"datetime":return(0,t.jsx)(d.TextControl,{__nextHasNoMarginBottom:!0,type:"datetime-local",label:e.label,value:r||"",onChange:i});case"tiers":return(0,t.jsx)(u,{field:e,value:r||[],onChange:i});default:return(0,t.jsx)(d.TextControl,{__nextHasNoMarginBottom:!0,label:e.label,value:r??"",onChange:i})}}function j({label:e,choices:n,value:r,onChange:i}){const o=Object.fromEntries(Object.entries(n).map(([e,n])=>[n,e]));return(0,t.jsx)(d.FormTokenField,{__nextHasNoMarginBottom:!0,__experimentalExpandOnFocus:!0,label:e,value:r.map(e=>n[e]||e),suggestions:Object.values(n),onChange:e=>i(e.map(e=>o[e]).filter(Boolean))})}function m({label:e,value:i,onChange:o}){const[a,s]=(0,n.useState)({});return(0,n.useEffect)(()=>{c()({path:"/wp/v2/product_cat?per_page=100&_fields=id,name"}).then(e=>s(Object.fromEntries(e.map(e=>[e.id,e.name])))).catch(()=>s({}))},[]),(0,t.jsx)(j,{label:e||(0,r.__)("Categories","joinery-core"),choices:a,value:i.map(String),onChange:e=>o(e.map(Number))})}function y({label:e,help:r,value:i,onChange:o}){const[a,s]=(0,n.useState)([]),l=e=>e.map(e=>({value:String(e.id),label:e.name})),u=e=>c()({path:`/wc/v3/products?status=publish&type=simple&per_page=20&_fields=id,name&search=${encodeURIComponent(e||"")}`}).then(e=>s(n=>[...n.filter(e=>e.value===String(i)),...l(e).filter(e=>e.value!==String(i))])).catch(()=>{});return(0,n.useEffect)(()=>{i&&c()({path:`/wc/v3/products/${Number(i)}?_fields=id,name`}).then(e=>s(n=>[...l([e]),...n])).catch(()=>{}),u("")},[]),(0,t.jsx)(d.ComboboxControl,{__nextHasNoMarginBottom:!0,label:e,help:r,value:i?String(i):null,options:a,onFilterValueChange:u,onChange:e=>o(e?Number(e):"")})}const p="joinery-more-rule-types-hidden",_=()=>{try{return"1"===window.localStorage.getItem(p)}catch{return!1}};function x({schema:e}){const[i,o]=(0,n.useState)(_),a=[...e.conditions,...e.actions].map(e=>e.id),l=[{prefix:"dp_",name:(0,r.__)("Dynamic Pricing & Discounts","joinery-core"),text:(0,r.__)("Quantity tiers, buy X get Y, free gifts, fixed prices, weekdays and first orders.","joinery-core"),url:"https://www.joinerywp.com/plugins/dynamic-pricing"},{prefix:"b2b_",name:(0,r.__)("B2B & Wholesale","joinery-core"),text:(0,r.__)("Rules for trade tiers such as Wholesale or Gold.","joinery-core"),url:"https://www.joinerywp.com/plugins/b2b-wholesale"},{prefix:"gc_",name:(0,r.__)("Gift Cards & Store Credit","joinery-core"),text:(0,r.__)("Give store credit after an order.","joinery-core"),url:"https://www.joinerywp.com/plugins/gift-cards"},{prefix:"pr_",name:(0,r.__)("Points & Rewards","joinery-core"),text:(0,r.__)("Give bonus points after an order.","joinery-core"),url:"https://www.joinerywp.com/plugins/points-rewards"}].filter(e=>!a.some(n=>n.startsWith(e.prefix)));return i||0===l.length?null:(0,t.jsxs)(s,{className:"joinery-more",children:[(0,t.jsx)("p",{className:"joinery-eyebrow",children:(0,r.__)("More rule types","joinery-core")}),(0,t.jsx)("p",{className:"joinery-more__intro",children:(0,r.__)("Paid Joinery plugins add their conditions and actions to this screen.","joinery-core")}),(0,t.jsx)("ul",{children:l.map(e=>(0,t.jsxs)("li",{children:[(0,t.jsx)(d.ExternalLink,{href:e.url,children:e.name}),(0,t.jsx)("span",{children:e.text})]},e.prefix))}),(0,t.jsx)(d.Button,{variant:"link",onClick:()=>{try{window.localStorage.setItem(p,"1")}catch{}o(!0)},children:(0,r.__)("Hide this","joinery-core")})]})}function g(){const[e,i]=(0,n.useState)(null),[o,l]=(0,n.useState)([]),[u,h]=(0,n.useState)(null),[j,m]=(0,n.useState)(""),y=()=>Promise.all([c()({path:"/joinery/v1/schema"}),c()({path:"/joinery/v1/rules"})]).then(([e,n])=>{i(e),l(n),h(e=>e||n[0]||{id:0,name:"",active:!0,priority:10,exclusive:!1,conditions:[],actions:[]})}).catch(e=>m(e.message));return(0,n.useEffect)(()=>{y()},[]),j?(0,t.jsx)(d.Notice,{status:"error",isDismissible:!1,children:j}):e?(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(a,{eyebrow:(0,r.__)("Rules","joinery-core"),title:(0,r.__)("Every price, discount and reward rule.","joinery-core"),description:(0,r.__)("Rules here are shared by all your Joinery plugins.","joinery-core"),action:(0,t.jsx)(d.Button,{variant:"primary",onClick:()=>h({id:0,name:"",active:!0,priority:10,exclusive:!1,conditions:[],actions:[]}),children:(0,r.__)("Add rule","joinery-core")})}),(0,t.jsxs)("div",{className:"joinery-rules",children:[(0,t.jsxs)("div",{className:"joinery-rules__side",children:[(0,t.jsxs)(s,{className:"joinery-rules__list",children:[(0,t.jsxs)("p",{className:"joinery-eyebrow",children:[(0,r.__)("All rules","joinery-core")," (",o.length,")"]}),0===o.length&&(0,t.jsx)("p",{children:(0,r.__)("No rules yet. Add your first one.","joinery-core")}),o.map(e=>(0,t.jsxs)("button",{type:"button",className:"joinery-rules__item","aria-current":u?.id===e.id?"true":void 0,onClick:()=>h(e),children:[(0,t.jsx)("strong",{children:e.name}),(0,t.jsx)("span",{children:e.active?(0,r.__)("Active","joinery-core"):(0,r.__)("Off","joinery-core")})]},e.id))]}),(0,t.jsx)(x,{schema:e})]}),u&&(0,t.jsx)(b,{rule:u,schema:e,onSaved:e=>{h(e),y()}},u.id||"new")]})]}):(0,t.jsx)(d.Spinner,{})}function b({rule:e,schema:i,onSaved:o}){const[a,l]=(0,n.useState)(e),[u,h]=(0,n.useState)(!1),[j,m]=(0,n.useState)(null),y=e=>l(n=>({...n,...e}));return(0,t.jsxs)(s,{className:"joinery-editor",children:[(0,t.jsxs)("div",{className:"joinery-editor__head",children:[(0,t.jsx)(d.TextControl,{__nextHasNoMarginBottom:!0,label:(0,r.__)("Rule name","joinery-core"),value:a.name,onChange:e=>y({name:e})}),(0,t.jsx)(d.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,r.__)("Rule is active","joinery-core"),checked:a.active,onChange:e=>y({active:e})})]}),(0,t.jsx)(v,{title:(0,r.__)("When all of these are true","joinery-core"),addLabel:(0,r.__)("+ Add condition","joinery-core"),parts:a.conditions,available:i.conditions,schema:i,onChange:e=>y({conditions:e})}),(0,t.jsx)(v,{title:(0,r.__)("Then do this","joinery-core"),addLabel:(0,r.__)("+ Add action","joinery-core"),parts:a.actions,available:i.actions,schema:i,onChange:e=>y({actions:e})}),(0,t.jsxs)("div",{className:"joinery-editor__foot",children:[(0,t.jsx)(d.TextControl,{__nextHasNoMarginBottom:!0,type:"number",label:(0,r.__)("Priority (lower runs first)","joinery-core"),value:String(a.priority),onChange:e=>y({priority:Number(e)||0})}),(0,t.jsx)(d.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,r.__)("Don't combine with other rules","joinery-core"),checked:a.exclusive,onChange:e=>y({exclusive:e})}),(0,t.jsx)(d.Button,{variant:"primary",onClick:()=>{h(!0),m(null),c()({path:a.id?`/joinery/v1/rules/${a.id}`:"/joinery/v1/rules",method:a.id?"PUT":"POST",data:a}).then(e=>{m({status:"success",text:(0,r.__)("Rule saved.","joinery-core")}),o(e)}).catch(e=>m({status:"error",text:e.message})).finally(()=>h(!1))},isBusy:u,disabled:u,children:(0,r.__)("Save rule","joinery-core")})]}),j&&(0,t.jsx)(d.Notice,{status:j.status,onRemove:()=>m(null),children:j.text})]})}function v({title:e,addLabel:n,parts:i,available:o,schema:a,onChange:s}){const l=(e,n)=>s(i.map((r,t)=>t===e?n:r));return(0,t.jsxs)("div",{className:"joinery-parts",children:[(0,t.jsx)("p",{className:"joinery-eyebrow",children:e}),i.map((e,n)=>{const c=o.find(n=>n.id===e.type);return(0,t.jsxs)("div",{className:"joinery-part",children:[(0,t.jsx)(d.SelectControl,{__nextHasNoMarginBottom:!0,label:(0,r.__)("Type","joinery-core"),value:e.type,options:o.map(e=>({value:e.id,label:e.label})),onChange:e=>l(n,{type:e,settings:{}})}),c?.fields.map(r=>(0,t.jsx)(h,{field:r,schema:a,value:e.settings[r.key]??r.default,onChange:t=>l(n,{...e,settings:{...e.settings,[r.key]:t}})},r.key)),(0,t.jsx)(d.Button,{variant:"link",isDestructive:!0,onClick:()=>(e=>s(i.filter((n,r)=>r!==e)))(n),children:(0,r.__)("Remove","joinery-core")})]},n)}),(0,t.jsx)(d.Button,{variant:"secondary",onClick:()=>o[0]&&s([...i,{type:o[0].id,settings:{}}]),children:n})]})}const f=[{id:"rules",label:(0,r.__)("Rules","joinery-core"),render:()=>(0,t.jsx)(g,{})}];window.joinery=window.joinery||{},window.joinery.registerTab=e=>f.push(e),window.joinery.ui={Card:s,PageIntro:a,Tag:function({tone:e="neutral",children:n}){return(0,t.jsx)("span",{className:`joinery-tag joinery-tag--${e}`,children:n})}},document.addEventListener("DOMContentLoaded",()=>{const e=document.getElementById("joinery-admin-root");e&&(0,n.createRoot)(e).render((0,t.jsx)(o,{tabs:f}))})})();
  • joinery-core/tags/0.4.0/changelog.txt

    r3767875 r3769397  
    11*** Joinery Core Changelog ***
     2
     32026-10-27 - version 0.4.0
     4* New: A “More rule types” panel on Joinery → Rules links to the paid plugins that add rule types. It lists only plugins whose rule types aren’t registered, and “Hide this” keeps it hidden in that browser.
    25
    362026-10-19 - version 0.3.0
  • joinery-core/tags/0.4.0/joinery-core.php

    r3767875 r3769397  
    33 * Plugin Name:          Joinery Core
    44 * Description:          The shared core for Joinery plugins: one rules engine, one pricing pipeline and one customer wallet. Includes free discount rules.
    5  * Version:              0.3.0
     5 * Version:              0.4.0
    66 * Requires at least:    6.5
    77 * Requires PHP:         8.1
    … …  
    2222defined( 'ABSPATH' ) || exit;
    2323
    24 define( 'JOINERY_CORE_VERSION', '0.3.0' );
     24define( 'JOINERY_CORE_VERSION', '0.4.0' );
    2525define( 'JOINERY_CORE_FILE', __FILE__ );
    2626
  • joinery-core/tags/0.4.0/readme.txt

    r3769298 r3769397  
    55Tested up to: 7.1
    66Requires PHP: 8.1
    7 Stable tag: 0.3.0
     7Stable tag: 0.4.0
    88License: GPLv2 or later
    99License URI: https://www.gnu.org/licenses/gpl-2.0.html
    … …  
    4343= Do I need a paid plugin? =
    4444
    45 No. Joinery Core works on its own. Paid Joinery plugins add more rule types, such as tiered pricing, wholesale accounts and gift cards, to the same rules screen.
     45No. Joinery Core works on its own. Paid Joinery plugins add more rule types, such as tiered pricing, wholesale accounts and gift cards, to the same rules screen. A small “More rule types” panel there lists the ones you don’t have; you can hide it.
    4646
    4747= Does it slow down my store? =
    … …  
    6262== Changelog ==
    6363
     64= 0.4.0 =
     65* New: A small “More rule types” panel on Joinery → Rules lists the paid Joinery plugins that add rule types. It only shows plugins you don’t have, and you can hide it.
     66
    6467= 0.3.0 =
    6568* New: Rules can tell whether the customer has ordered before, for first-order offers.
  • joinery-core/trunk/assets/packages/rules-ui/src/index.js

    r3767875 r3769397  
    1717import { Card, PageIntro } from '@joinery/admin-ui';
    1818import { FieldControl } from './fields';
     19import { MoreRuleTypes } from './more';
    1920import './rules.css';
    2021
    … …  
    7172                        />
    7273                        <div className="joinery-rules">
    73                                 <Card className="joinery-rules__list">
    74                                         <p className="joinery-eyebrow">
    75                                                 { __( 'All rules', 'joinery-core' ) } ({ rules.length })
    76                                         </p>
    77                                         { rules.length === 0 && <p>{ __( 'No rules yet. Add your first one.', 'joinery-core' ) }</p> }
    78                                         { rules.map( ( rule ) => (
    79                                                 <button
    80                                                         key={ rule.id }
    81                                                         type="button"
    82                                                         className="joinery-rules__item"
    83                                                         aria-current={ editing?.id === rule.id ? 'true' : undefined }
    84                                                         onClick={ () => setEditing( rule ) }
    85                                                 >
    86                                                         <strong>{ rule.name }</strong>
    87                                                         <span>{ rule.active ? __( 'Active', 'joinery-core' ) : __( 'Off', 'joinery-core' ) }</span>
    88                                                 </button>
    89                                         ) ) }
    90                                 </Card>
     74                                <div className="joinery-rules__side">
     75                                        <Card className="joinery-rules__list">
     76                                                <p className="joinery-eyebrow">
     77                                                        { __( 'All rules', 'joinery-core' ) } ({ rules.length })
     78                                                </p>
     79                                                { rules.length === 0 && <p>{ __( 'No rules yet. Add your first one.', 'joinery-core' ) }</p> }
     80                                                { rules.map( ( rule ) => (
     81                                                        <button
     82                                                                key={ rule.id }
     83                                                                type="button"
     84                                                                className="joinery-rules__item"
     85                                                                aria-current={ editing?.id === rule.id ? 'true' : undefined }
     86                                                                onClick={ () => setEditing( rule ) }
     87                                                        >
     88                                                                <strong>{ rule.name }</strong>
     89                                                                <span>{ rule.active ? __( 'Active', 'joinery-core' ) : __( 'Off', 'joinery-core' ) }</span>
     90                                                        </button>
     91                                                ) ) }
     92                                        </Card>
     93                                        <MoreRuleTypes schema={ schema } />
     94                                </div>
    9195                                { editing && (
    9296                                        <RuleEditor
  • joinery-core/trunk/assets/packages/rules-ui/src/rules.css

    r3767875 r3769397  
    66}
    77
    8 .joinery-rules__list {
     8.joinery-rules__side {
    99  flex: 1 1 280px;
    1010  max-width: 360px;
     11  display: flex;
     12  flex-direction: column;
     13  gap: 20px;
     14}
     15
     16.joinery-rules__list {
    1117  display: flex;
    1218  flex-direction: column;
    … …  
    3642.joinery-rules__item[aria-current="true"] {
    3743  background: var(--joinery-field);
     44}
     45
     46.joinery-more {
     47  display: flex;
     48  flex-direction: column;
     49  align-items: flex-start;
     50  gap: 10px;
     51  color: var(--joinery-ink-2);
     52}
     53
     54.joinery-more p,
     55.joinery-more ul {
     56  margin: 0;
     57}
     58
     59.joinery-more ul {
     60  display: flex;
     61  flex-direction: column;
     62  gap: 12px;
     63}
     64
     65.joinery-more li {
     66  display: flex;
     67  flex-direction: column;
     68  gap: 2px;
     69  margin: 0;
     70}
     71
     72.joinery-more li span {
     73  color: var(--joinery-muted);
     74  font-size: 13px;
    3875}
    3976
  • joinery-core/trunk/build/admin-rtl.css

    r3767875 r3769397  
    1 .joinery-rules{align-items:flex-start;display:flex;flex-wrap:wrap;gap:20px}.joinery-rules__list{display:flex;flex:1 1 280px;flex-direction:column;gap:8px;max-width:360px}.joinery-rules__item{background:transparent;border:0;border-radius:12px;color:var(--joinery-ink);cursor:pointer;display:flex;font:inherit;gap:12px;justify-content:space-between;padding:12px 14px;text-align:right;width:100%}.joinery-rules__item span{color:var(--joinery-muted);font-size:12px}.joinery-rules__item[aria-current=true]{background:var(--joinery-field)}.joinery-editor{display:flex;flex:999 1 520px;flex-direction:column;gap:22px;min-width:0}.joinery-editor__foot,.joinery-editor__head{align-items:flex-end;display:flex;flex-wrap:wrap;gap:16px;justify-content:space-between}.joinery-editor__foot{border-top:1px solid var(--joinery-line);padding-top:18px}.joinery-parts{flex-direction:column;gap:10px}.joinery-part,.joinery-parts{align-items:flex-start;display:flex}.joinery-part{background:var(--joinery-field);border-radius:14px;box-sizing:border-box;flex-wrap:wrap;gap:12px;padding:14px;width:100%}.joinery-part>.components-button.is-link{margin-top:23px;min-height:40px}.joinery-tiers{border:0;display:flex;flex-direction:column;gap:8px;margin:0;padding:0}.joinery-tiers__row{align-items:flex-end;display:flex;gap:10px}.joinery-editor__head>.components-base-control{flex:1 1 320px;max-width:520px}
     1.joinery-rules{align-items:flex-start;display:flex;flex-wrap:wrap;gap:20px}.joinery-rules__side{display:flex;flex:1 1 280px;flex-direction:column;gap:20px;max-width:360px}.joinery-rules__list{display:flex;flex-direction:column;gap:8px}.joinery-rules__item{background:transparent;border:0;border-radius:12px;color:var(--joinery-ink);cursor:pointer;display:flex;font:inherit;gap:12px;justify-content:space-between;padding:12px 14px;text-align:right;width:100%}.joinery-rules__item span{color:var(--joinery-muted);font-size:12px}.joinery-rules__item[aria-current=true]{background:var(--joinery-field)}.joinery-more{align-items:flex-start;color:var(--joinery-ink-2);display:flex;flex-direction:column;gap:10px}.joinery-more p,.joinery-more ul{margin:0}.joinery-more ul{gap:12px}.joinery-more li,.joinery-more ul{display:flex;flex-direction:column}.joinery-more li{gap:2px;margin:0}.joinery-more li span{color:var(--joinery-muted);font-size:13px}.joinery-editor{display:flex;flex:999 1 520px;flex-direction:column;gap:22px;min-width:0}.joinery-editor__foot,.joinery-editor__head{align-items:flex-end;display:flex;flex-wrap:wrap;gap:16px;justify-content:space-between}.joinery-editor__foot{border-top:1px solid var(--joinery-line);padding-top:18px}.joinery-parts{flex-direction:column;gap:10px}.joinery-part,.joinery-parts{align-items:flex-start;display:flex}.joinery-part{background:var(--joinery-field);border-radius:14px;box-sizing:border-box;flex-wrap:wrap;gap:12px;padding:14px;width:100%}.joinery-part>.components-button.is-link{margin-top:23px;min-height:40px}.joinery-tiers{border:0;display:flex;flex-direction:column;gap:8px;margin:0;padding:0}.joinery-tiers__row{align-items:flex-end;display:flex;gap:10px}.joinery-editor__head>.components-base-control{flex:1 1 320px;max-width:520px}
    22.joinery-app{--joinery-ink:#1b1e16;--joinery-ink-2:#545a4a;--joinery-muted:#6e7462;--joinery-line:#ecebe0;--joinery-field:#f5f4ec;--joinery-surface:#fff;--joinery-forest:#1f2419;--joinery-clay:#a85a33;--joinery-gradient:linear-gradient(115deg,#efe8d2 25%,#b8be8a 62%,#7f8a52);--joinery-sans:"Geist",system-ui,-apple-system,"Segoe UI",sans-serif;--joinery-mono:"Geist Mono",ui-monospace,Menlo,monospace;color:var(--joinery-ink);display:flex;flex-direction:column;font-family:var(--joinery-sans);font-size:14px;gap:20px;margin-top:16px}.joinery-card{background:var(--joinery-surface);border-radius:20px;box-shadow:0 0 0 1px rgba(27,30,22,.06),0 1px 3px rgba(27,30,22,.06);padding:20px 24px}.joinery-header{flex-wrap:wrap;gap:16px;justify-content:space-between;padding:14px 20px}.joinery-brand,.joinery-header{align-items:center;display:flex}.joinery-brand{font-size:19px;font-weight:600;gap:10px;letter-spacing:-.02em}.joinery-tabs{background:var(--joinery-line);border-radius:999px;display:flex;flex-wrap:wrap;gap:4px;padding:4px}.joinery-tab{background:transparent;border:0;border-radius:999px;color:var(--joinery-ink-2);cursor:pointer;font:500 14px var(--joinery-sans);padding:8px 16px}.joinery-tab[aria-current=page]{background:var(--joinery-surface);box-shadow:0 0 0 1px rgba(27,30,22,.08),0 1px 2px rgba(27,30,22,.1);color:var(--joinery-ink)}.joinery-intro{align-items:flex-end;background:var(--joinery-gradient);border-radius:20px;display:flex;flex-wrap:wrap;gap:16px;justify-content:space-between;padding:28px}.joinery-intro h1{color:var(--joinery-ink);font:500 36px/1.05 var(--joinery-sans);letter-spacing:-.04em;margin:4px 0;padding:0}.joinery-intro__text{color:rgba(27,30,22,.75);font-size:15px;font-weight:500;margin:0}.joinery-eyebrow{color:var(--joinery-muted);font:500 12px var(--joinery-mono);letter-spacing:.12em;margin:0;text-transform:uppercase}.joinery-intro .joinery-eyebrow{color:rgba(27,30,22,.7)}.joinery-tag{border-radius:6px;display:inline-block;font-size:12px;font-weight:500;padding:2px 8px}.joinery-tag--pricing{background:#e5ead0;color:#4b5b1f}.joinery-tag--b2b{background:#dde8e5;color:#2e5650}.joinery-tag--wallet{background:#f5d9c8;color:#8c3a1a}.joinery-tag--options{background:#f4e3bc;color:#7a5410}.joinery-tag--neutral{background:var(--joinery-line);color:var(--joinery-ink-2)}.joinery-app .components-button.is-primary{background:var(--joinery-ink);border-radius:999px;min-height:40px;padding:0 20px}.joinery-app .components-button.is-primary:hover:not(:disabled){background:#2d3425}.joinery-app .components-button.is-secondary{border-radius:999px;box-shadow:inset 0 0 0 1px rgba(27,30,22,.2);color:var(--joinery-ink);min-height:40px}.joinery-app .components-base-control__field input,.joinery-app .components-base-control__field select,.joinery-app .components-select-control__input,.joinery-app .components-text-control__input{border-radius:999px}
  • joinery-core/trunk/build/admin.asset.php

    r3767875 r3769397  
    77                'wp-i18n'
    88        ),
    9         'version' => '2ec3f350f6eb4ce8ba7f'
     9        'version' => '27bfce624f7c742c41be'
    1010);
  • joinery-core/trunk/build/admin.css

    r3767875 r3769397  
    1 .joinery-rules{align-items:flex-start;display:flex;flex-wrap:wrap;gap:20px}.joinery-rules__list{display:flex;flex:1 1 280px;flex-direction:column;gap:8px;max-width:360px}.joinery-rules__item{background:transparent;border:0;border-radius:12px;color:var(--joinery-ink);cursor:pointer;display:flex;font:inherit;gap:12px;justify-content:space-between;padding:12px 14px;text-align:left;width:100%}.joinery-rules__item span{color:var(--joinery-muted);font-size:12px}.joinery-rules__item[aria-current=true]{background:var(--joinery-field)}.joinery-editor{display:flex;flex:999 1 520px;flex-direction:column;gap:22px;min-width:0}.joinery-editor__foot,.joinery-editor__head{align-items:flex-end;display:flex;flex-wrap:wrap;gap:16px;justify-content:space-between}.joinery-editor__foot{border-top:1px solid var(--joinery-line);padding-top:18px}.joinery-parts{flex-direction:column;gap:10px}.joinery-part,.joinery-parts{align-items:flex-start;display:flex}.joinery-part{background:var(--joinery-field);border-radius:14px;box-sizing:border-box;flex-wrap:wrap;gap:12px;padding:14px;width:100%}.joinery-part>.components-button.is-link{margin-top:23px;min-height:40px}.joinery-tiers{border:0;display:flex;flex-direction:column;gap:8px;margin:0;padding:0}.joinery-tiers__row{align-items:flex-end;display:flex;gap:10px}.joinery-editor__head>.components-base-control{flex:1 1 320px;max-width:520px}
     1.joinery-rules{align-items:flex-start;display:flex;flex-wrap:wrap;gap:20px}.joinery-rules__side{display:flex;flex:1 1 280px;flex-direction:column;gap:20px;max-width:360px}.joinery-rules__list{display:flex;flex-direction:column;gap:8px}.joinery-rules__item{background:transparent;border:0;border-radius:12px;color:var(--joinery-ink);cursor:pointer;display:flex;font:inherit;gap:12px;justify-content:space-between;padding:12px 14px;text-align:left;width:100%}.joinery-rules__item span{color:var(--joinery-muted);font-size:12px}.joinery-rules__item[aria-current=true]{background:var(--joinery-field)}.joinery-more{align-items:flex-start;color:var(--joinery-ink-2);display:flex;flex-direction:column;gap:10px}.joinery-more p,.joinery-more ul{margin:0}.joinery-more ul{gap:12px}.joinery-more li,.joinery-more ul{display:flex;flex-direction:column}.joinery-more li{gap:2px;margin:0}.joinery-more li span{color:var(--joinery-muted);font-size:13px}.joinery-editor{display:flex;flex:999 1 520px;flex-direction:column;gap:22px;min-width:0}.joinery-editor__foot,.joinery-editor__head{align-items:flex-end;display:flex;flex-wrap:wrap;gap:16px;justify-content:space-between}.joinery-editor__foot{border-top:1px solid var(--joinery-line);padding-top:18px}.joinery-parts{flex-direction:column;gap:10px}.joinery-part,.joinery-parts{align-items:flex-start;display:flex}.joinery-part{background:var(--joinery-field);border-radius:14px;box-sizing:border-box;flex-wrap:wrap;gap:12px;padding:14px;width:100%}.joinery-part>.components-button.is-link{margin-top:23px;min-height:40px}.joinery-tiers{border:0;display:flex;flex-direction:column;gap:8px;margin:0;padding:0}.joinery-tiers__row{align-items:flex-end;display:flex;gap:10px}.joinery-editor__head>.components-base-control{flex:1 1 320px;max-width:520px}
    22.joinery-app{--joinery-ink:#1b1e16;--joinery-ink-2:#545a4a;--joinery-muted:#6e7462;--joinery-line:#ecebe0;--joinery-field:#f5f4ec;--joinery-surface:#fff;--joinery-forest:#1f2419;--joinery-clay:#a85a33;--joinery-gradient:linear-gradient(115deg,#efe8d2 25%,#b8be8a 62%,#7f8a52);--joinery-sans:"Geist",system-ui,-apple-system,"Segoe UI",sans-serif;--joinery-mono:"Geist Mono",ui-monospace,Menlo,monospace;color:var(--joinery-ink);display:flex;flex-direction:column;font-family:var(--joinery-sans);font-size:14px;gap:20px;margin-top:16px}.joinery-card{background:var(--joinery-surface);border-radius:20px;box-shadow:0 0 0 1px rgba(27,30,22,.06),0 1px 3px rgba(27,30,22,.06);padding:20px 24px}.joinery-header{flex-wrap:wrap;gap:16px;justify-content:space-between;padding:14px 20px}.joinery-brand,.joinery-header{align-items:center;display:flex}.joinery-brand{font-size:19px;font-weight:600;gap:10px;letter-spacing:-.02em}.joinery-tabs{background:var(--joinery-line);border-radius:999px;display:flex;flex-wrap:wrap;gap:4px;padding:4px}.joinery-tab{background:transparent;border:0;border-radius:999px;color:var(--joinery-ink-2);cursor:pointer;font:500 14px var(--joinery-sans);padding:8px 16px}.joinery-tab[aria-current=page]{background:var(--joinery-surface);box-shadow:0 0 0 1px rgba(27,30,22,.08),0 1px 2px rgba(27,30,22,.1);color:var(--joinery-ink)}.joinery-intro{align-items:flex-end;background:var(--joinery-gradient);border-radius:20px;display:flex;flex-wrap:wrap;gap:16px;justify-content:space-between;padding:28px}.joinery-intro h1{color:var(--joinery-ink);font:500 36px/1.05 var(--joinery-sans);letter-spacing:-.04em;margin:4px 0;padding:0}.joinery-intro__text{color:rgba(27,30,22,.75);font-size:15px;font-weight:500;margin:0}.joinery-eyebrow{color:var(--joinery-muted);font:500 12px var(--joinery-mono);letter-spacing:.12em;margin:0;text-transform:uppercase}.joinery-intro .joinery-eyebrow{color:rgba(27,30,22,.7)}.joinery-tag{border-radius:6px;display:inline-block;font-size:12px;font-weight:500;padding:2px 8px}.joinery-tag--pricing{background:#e5ead0;color:#4b5b1f}.joinery-tag--b2b{background:#dde8e5;color:#2e5650}.joinery-tag--wallet{background:#f5d9c8;color:#8c3a1a}.joinery-tag--options{background:#f4e3bc;color:#7a5410}.joinery-tag--neutral{background:var(--joinery-line);color:var(--joinery-ink-2)}.joinery-app .components-button.is-primary{background:var(--joinery-ink);border-radius:999px;min-height:40px;padding:0 20px}.joinery-app .components-button.is-primary:hover:not(:disabled){background:#2d3425}.joinery-app .components-button.is-secondary{border-radius:999px;box-shadow:inset 0 0 0 1px rgba(27,30,22,.2);color:var(--joinery-ink);min-height:40px}.joinery-app .components-base-control__field input,.joinery-app .components-base-control__field select,.joinery-app .components-select-control__input,.joinery-app .components-text-control__input{border-radius:999px}
  • joinery-core/trunk/build/admin.js

    r3767875 r3769397  
    1 (()=>{"use strict";const e={n:n=>{const t=n&&n.__esModule?()=>n.default:()=>n;return e.d(t,{a:t}),t},d:(n,t)=>{for(var r in t)e.o(t,r)&&!e.o(n,r)&&Object.defineProperty(n,r,{enumerable:!0,get:t[r]})},o:(e,n)=>Object.hasOwn(e,n)},n=window.wp.element,t=window.wp.i18n,r=window.ReactJSXRuntime;function o(){return(0,r.jsxs)("svg",{width:"26",height:"26",viewBox:"0 0 28 28","aria-hidden":"true",children:[(0,r.jsx)("rect",{x:"2",y:"3",width:"24",height:"9",rx:"2",fill:"currentColor"}),(0,r.jsx)("path",{d:"M9 12 L5 25 H23 L19 12 Z",fill:"currentColor",fillOpacity:"0.55"})]})}function a({tabs:e}){const a=()=>window.location.hash.replace(/^#/,""),[i,s]=(0,n.useState)(()=>a()||e[0]?.id),l=e.find(e=>e.id===i)||e[0];return(0,n.useEffect)(()=>{const n=()=>s(a()||e[0]?.id);return window.addEventListener("hashchange",n),()=>window.removeEventListener("hashchange",n)},[e]),(0,r.jsxs)("div",{className:"joinery-app",children:[(0,r.jsxs)("header",{className:"joinery-card joinery-header",children:[(0,r.jsxs)("div",{className:"joinery-brand",children:[(0,r.jsx)(o,{}),(0,r.jsx)("span",{children:(0,t.__)("Joinery","joinery-core")})]}),(0,r.jsx)("nav",{className:"joinery-tabs","aria-label":(0,t.__)("Joinery sections","joinery-core"),children:e.map(e=>(0,r.jsx)("button",{type:"button",className:"joinery-tab","aria-current":e.id===l?.id?"page":void 0,onClick:()=>{return n=e.id,s(n),void window.history.replaceState(null,"",`#${n}`);var n},children:e.label},e.id))})]}),l?.render()]})}function i({eyebrow:e,title:n,description:t,action:o}){return(0,r.jsxs)("section",{className:"joinery-intro",children:[(0,r.jsxs)("div",{children:[(0,r.jsx)("p",{className:"joinery-eyebrow",children:e}),(0,r.jsx)("h1",{children:n}),t&&(0,r.jsx)("p",{className:"joinery-intro__text",children:t})]}),o]})}function s({className:e="",children:n,...t}){return(0,r.jsx)("section",{className:`joinery-card ${e}`,...t,children:n})}const l=window.wp.apiFetch;var c=e.n(l);const d=window.wp.components;function u({field:e,value:n,onChange:o}){const a=(e,t)=>o(n.map((n,r)=>r===e?{...n,...t}:n));return(0,r.jsxs)("fieldset",{className:"joinery-tiers",children:[(0,r.jsx)("legend",{children:e.label}),n.map((e,i)=>(0,r.jsxs)("div",{className:"joinery-tiers__row",children:[(0,r.jsx)(d.TextControl,{__nextHasNoMarginBottom:!0,type:"number",label:(0,t.__)("From quantity","joinery-core"),value:e.min??"",onChange:e=>a(i,{min:Number(e)})}),(0,r.jsx)(d.TextControl,{__nextHasNoMarginBottom:!0,type:"number",label:(0,t.__)("% off","joinery-core"),value:e.percent??"",onChange:e=>a(i,{percent:Number(e)})}),(0,r.jsx)(d.Button,{variant:"link",isDestructive:!0,onClick:()=>o(n.filter((e,n)=>n!==i)),children:(0,t.__)("Remove","joinery-core")})]},i)),(0,r.jsx)(d.Button,{variant:"secondary",onClick:()=>o([...n,{min:"",percent:""}]),children:(0,t.__)("+ Add tier","joinery-core")})]})}function h({field:e,schema:n,value:t,onChange:o}){switch(e.type){case"number":case"money":case"percent":return(0,r.jsx)(d.TextControl,{__nextHasNoMarginBottom:!0,type:"number",label:"money"===e.type?`${e.label} (${n.currency})`:"percent"===e.type?`${e.label} (%)`:e.label,help:e.help,min:e.min,max:e.max,value:t??"",onChange:e=>o(""===e?"":Number(e))});case"select":return(0,r.jsx)(d.SelectControl,{__nextHasNoMarginBottom:!0,label:e.label,help:e.help,value:t??Object.keys(e.options||{})[0],options:Object.entries(e.options||{}).map(([e,n])=>({value:e,label:n})),onChange:o});case"role-select":return(0,r.jsx)(j,{label:e.label,choices:n.roles,value:t||[],onChange:o});case"category-select":return(0,r.jsx)(m,{label:e.label,value:t||[],onChange:o});case"product-select":return(0,r.jsx)(y,{label:e.label,help:e.help,value:t,onChange:o});case"datetime":return(0,r.jsx)(d.TextControl,{__nextHasNoMarginBottom:!0,type:"datetime-local",label:e.label,value:t||"",onChange:o});case"tiers":return(0,r.jsx)(u,{field:e,value:t||[],onChange:o});default:return(0,r.jsx)(d.TextControl,{__nextHasNoMarginBottom:!0,label:e.label,value:t??"",onChange:o})}}function j({label:e,choices:n,value:t,onChange:o}){const a=Object.fromEntries(Object.entries(n).map(([e,n])=>[n,e]));return(0,r.jsx)(d.FormTokenField,{__nextHasNoMarginBottom:!0,__experimentalExpandOnFocus:!0,label:e,value:t.map(e=>n[e]||e),suggestions:Object.values(n),onChange:e=>o(e.map(e=>a[e]).filter(Boolean))})}function m({label:e,value:o,onChange:a}){const[i,s]=(0,n.useState)({});return(0,n.useEffect)(()=>{c()({path:"/wp/v2/product_cat?per_page=100&_fields=id,name"}).then(e=>s(Object.fromEntries(e.map(e=>[e.id,e.name])))).catch(()=>s({}))},[]),(0,r.jsx)(j,{label:e||(0,t.__)("Categories","joinery-core"),choices:i,value:o.map(String),onChange:e=>a(e.map(Number))})}function y({label:e,help:t,value:o,onChange:a}){const[i,s]=(0,n.useState)([]),l=e=>e.map(e=>({value:String(e.id),label:e.name})),u=e=>c()({path:`/wc/v3/products?status=publish&type=simple&per_page=20&_fields=id,name&search=${encodeURIComponent(e||"")}`}).then(e=>s(n=>[...n.filter(e=>e.value===String(o)),...l(e).filter(e=>e.value!==String(o))])).catch(()=>{});return(0,n.useEffect)(()=>{o&&c()({path:`/wc/v3/products/${Number(o)}?_fields=id,name`}).then(e=>s(n=>[...l([e]),...n])).catch(()=>{}),u("")},[]),(0,r.jsx)(d.ComboboxControl,{__nextHasNoMarginBottom:!0,label:e,help:t,value:o?String(o):null,options:i,onFilterValueChange:u,onChange:e=>a(e?Number(e):"")})}function p(){const[e,o]=(0,n.useState)(null),[a,l]=(0,n.useState)([]),[u,h]=(0,n.useState)(null),[j,m]=(0,n.useState)(""),y=()=>Promise.all([c()({path:"/joinery/v1/schema"}),c()({path:"/joinery/v1/rules"})]).then(([e,n])=>{o(e),l(n),h(e=>e||n[0]||{id:0,name:"",active:!0,priority:10,exclusive:!1,conditions:[],actions:[]})}).catch(e=>m(e.message));return(0,n.useEffect)(()=>{y()},[]),j?(0,r.jsx)(d.Notice,{status:"error",isDismissible:!1,children:j}):e?(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(i,{eyebrow:(0,t.__)("Rules","joinery-core"),title:(0,t.__)("Every price, discount and reward rule.","joinery-core"),description:(0,t.__)("Rules here are shared by all your Joinery plugins.","joinery-core"),action:(0,r.jsx)(d.Button,{variant:"primary",onClick:()=>h({id:0,name:"",active:!0,priority:10,exclusive:!1,conditions:[],actions:[]}),children:(0,t.__)("Add rule","joinery-core")})}),(0,r.jsxs)("div",{className:"joinery-rules",children:[(0,r.jsxs)(s,{className:"joinery-rules__list",children:[(0,r.jsxs)("p",{className:"joinery-eyebrow",children:[(0,t.__)("All rules","joinery-core")," (",a.length,")"]}),0===a.length&&(0,r.jsx)("p",{children:(0,t.__)("No rules yet. Add your first one.","joinery-core")}),a.map(e=>(0,r.jsxs)("button",{type:"button",className:"joinery-rules__item","aria-current":u?.id===e.id?"true":void 0,onClick:()=>h(e),children:[(0,r.jsx)("strong",{children:e.name}),(0,r.jsx)("span",{children:e.active?(0,t.__)("Active","joinery-core"):(0,t.__)("Off","joinery-core")})]},e.id))]}),u&&(0,r.jsx)(x,{rule:u,schema:e,onSaved:e=>{h(e),y()}},u.id||"new")]})]}):(0,r.jsx)(d.Spinner,{})}function x({rule:e,schema:o,onSaved:a}){const[i,l]=(0,n.useState)(e),[u,h]=(0,n.useState)(!1),[j,m]=(0,n.useState)(null),y=e=>l(n=>({...n,...e}));return(0,r.jsxs)(s,{className:"joinery-editor",children:[(0,r.jsxs)("div",{className:"joinery-editor__head",children:[(0,r.jsx)(d.TextControl,{__nextHasNoMarginBottom:!0,label:(0,t.__)("Rule name","joinery-core"),value:i.name,onChange:e=>y({name:e})}),(0,r.jsx)(d.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,t.__)("Rule is active","joinery-core"),checked:i.active,onChange:e=>y({active:e})})]}),(0,r.jsx)(_,{title:(0,t.__)("When all of these are true","joinery-core"),addLabel:(0,t.__)("+ Add condition","joinery-core"),parts:i.conditions,available:o.conditions,schema:o,onChange:e=>y({conditions:e})}),(0,r.jsx)(_,{title:(0,t.__)("Then do this","joinery-core"),addLabel:(0,t.__)("+ Add action","joinery-core"),parts:i.actions,available:o.actions,schema:o,onChange:e=>y({actions:e})}),(0,r.jsxs)("div",{className:"joinery-editor__foot",children:[(0,r.jsx)(d.TextControl,{__nextHasNoMarginBottom:!0,type:"number",label:(0,t.__)("Priority (lower runs first)","joinery-core"),value:String(i.priority),onChange:e=>y({priority:Number(e)||0})}),(0,r.jsx)(d.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,t.__)("Don't combine with other rules","joinery-core"),checked:i.exclusive,onChange:e=>y({exclusive:e})}),(0,r.jsx)(d.Button,{variant:"primary",onClick:()=>{h(!0),m(null),c()({path:i.id?`/joinery/v1/rules/${i.id}`:"/joinery/v1/rules",method:i.id?"PUT":"POST",data:i}).then(e=>{m({status:"success",text:(0,t.__)("Rule saved.","joinery-core")}),a(e)}).catch(e=>m({status:"error",text:e.message})).finally(()=>h(!1))},isBusy:u,disabled:u,children:(0,t.__)("Save rule","joinery-core")})]}),j&&(0,r.jsx)(d.Notice,{status:j.status,onRemove:()=>m(null),children:j.text})]})}function _({title:e,addLabel:n,parts:o,available:a,schema:i,onChange:s}){const l=(e,n)=>s(o.map((t,r)=>r===e?n:t));return(0,r.jsxs)("div",{className:"joinery-parts",children:[(0,r.jsx)("p",{className:"joinery-eyebrow",children:e}),o.map((e,n)=>{const c=a.find(n=>n.id===e.type);return(0,r.jsxs)("div",{className:"joinery-part",children:[(0,r.jsx)(d.SelectControl,{__nextHasNoMarginBottom:!0,label:(0,t.__)("Type","joinery-core"),value:e.type,options:a.map(e=>({value:e.id,label:e.label})),onChange:e=>l(n,{type:e,settings:{}})}),c?.fields.map(t=>(0,r.jsx)(h,{field:t,schema:i,value:e.settings[t.key]??t.default,onChange:r=>l(n,{...e,settings:{...e.settings,[t.key]:r}})},t.key)),(0,r.jsx)(d.Button,{variant:"link",isDestructive:!0,onClick:()=>(e=>s(o.filter((n,t)=>t!==e)))(n),children:(0,t.__)("Remove","joinery-core")})]},n)}),(0,r.jsx)(d.Button,{variant:"secondary",onClick:()=>a[0]&&s([...o,{type:a[0].id,settings:{}}]),children:n})]})}const v=[{id:"rules",label:(0,t.__)("Rules","joinery-core"),render:()=>(0,r.jsx)(p,{})}];window.joinery=window.joinery||{},window.joinery.registerTab=e=>v.push(e),window.joinery.ui={Card:s,PageIntro:i,Tag:function({tone:e="neutral",children:n}){return(0,r.jsx)("span",{className:`joinery-tag joinery-tag--${e}`,children:n})}},document.addEventListener("DOMContentLoaded",()=>{const e=document.getElementById("joinery-admin-root");e&&(0,n.createRoot)(e).render((0,r.jsx)(a,{tabs:v}))})})();
     1(()=>{"use strict";const e={n:n=>{const r=n&&n.__esModule?()=>n.default:()=>n;return e.d(r,{a:r}),r},d:(n,r)=>{for(var t in r)e.o(r,t)&&!e.o(n,t)&&Object.defineProperty(n,t,{enumerable:!0,get:r[t]})},o:(e,n)=>Object.hasOwn(e,n)},n=window.wp.element,r=window.wp.i18n,t=window.ReactJSXRuntime;function i(){return(0,t.jsxs)("svg",{width:"26",height:"26",viewBox:"0 0 28 28","aria-hidden":"true",children:[(0,t.jsx)("rect",{x:"2",y:"3",width:"24",height:"9",rx:"2",fill:"currentColor"}),(0,t.jsx)("path",{d:"M9 12 L5 25 H23 L19 12 Z",fill:"currentColor",fillOpacity:"0.55"})]})}function o({tabs:e}){const o=()=>window.location.hash.replace(/^#/,""),[a,s]=(0,n.useState)(()=>o()||e[0]?.id),l=e.find(e=>e.id===a)||e[0];return(0,n.useEffect)(()=>{const n=()=>s(o()||e[0]?.id);return window.addEventListener("hashchange",n),()=>window.removeEventListener("hashchange",n)},[e]),(0,t.jsxs)("div",{className:"joinery-app",children:[(0,t.jsxs)("header",{className:"joinery-card joinery-header",children:[(0,t.jsxs)("div",{className:"joinery-brand",children:[(0,t.jsx)(i,{}),(0,t.jsx)("span",{children:(0,r.__)("Joinery","joinery-core")})]}),(0,t.jsx)("nav",{className:"joinery-tabs","aria-label":(0,r.__)("Joinery sections","joinery-core"),children:e.map(e=>(0,t.jsx)("button",{type:"button",className:"joinery-tab","aria-current":e.id===l?.id?"page":void 0,onClick:()=>{return n=e.id,s(n),void window.history.replaceState(null,"",`#${n}`);var n},children:e.label},e.id))})]}),l?.render()]})}function a({eyebrow:e,title:n,description:r,action:i}){return(0,t.jsxs)("section",{className:"joinery-intro",children:[(0,t.jsxs)("div",{children:[(0,t.jsx)("p",{className:"joinery-eyebrow",children:e}),(0,t.jsx)("h1",{children:n}),r&&(0,t.jsx)("p",{className:"joinery-intro__text",children:r})]}),i]})}function s({className:e="",children:n,...r}){return(0,t.jsx)("section",{className:`joinery-card ${e}`,...r,children:n})}const l=window.wp.apiFetch;var c=e.n(l);const d=window.wp.components;function u({field:e,value:n,onChange:i}){const o=(e,r)=>i(n.map((n,t)=>t===e?{...n,...r}:n));return(0,t.jsxs)("fieldset",{className:"joinery-tiers",children:[(0,t.jsx)("legend",{children:e.label}),n.map((e,a)=>(0,t.jsxs)("div",{className:"joinery-tiers__row",children:[(0,t.jsx)(d.TextControl,{__nextHasNoMarginBottom:!0,type:"number",label:(0,r.__)("From quantity","joinery-core"),value:e.min??"",onChange:e=>o(a,{min:Number(e)})}),(0,t.jsx)(d.TextControl,{__nextHasNoMarginBottom:!0,type:"number",label:(0,r.__)("% off","joinery-core"),value:e.percent??"",onChange:e=>o(a,{percent:Number(e)})}),(0,t.jsx)(d.Button,{variant:"link",isDestructive:!0,onClick:()=>i(n.filter((e,n)=>n!==a)),children:(0,r.__)("Remove","joinery-core")})]},a)),(0,t.jsx)(d.Button,{variant:"secondary",onClick:()=>i([...n,{min:"",percent:""}]),children:(0,r.__)("+ Add tier","joinery-core")})]})}function h({field:e,schema:n,value:r,onChange:i}){switch(e.type){case"number":case"money":case"percent":return(0,t.jsx)(d.TextControl,{__nextHasNoMarginBottom:!0,type:"number",label:"money"===e.type?`${e.label} (${n.currency})`:"percent"===e.type?`${e.label} (%)`:e.label,help:e.help,min:e.min,max:e.max,value:r??"",onChange:e=>i(""===e?"":Number(e))});case"select":return(0,t.jsx)(d.SelectControl,{__nextHasNoMarginBottom:!0,label:e.label,help:e.help,value:r??Object.keys(e.options||{})[0],options:Object.entries(e.options||{}).map(([e,n])=>({value:e,label:n})),onChange:i});case"role-select":return(0,t.jsx)(j,{label:e.label,choices:n.roles,value:r||[],onChange:i});case"category-select":return(0,t.jsx)(m,{label:e.label,value:r||[],onChange:i});case"product-select":return(0,t.jsx)(y,{label:e.label,help:e.help,value:r,onChange:i});case"datetime":return(0,t.jsx)(d.TextControl,{__nextHasNoMarginBottom:!0,type:"datetime-local",label:e.label,value:r||"",onChange:i});case"tiers":return(0,t.jsx)(u,{field:e,value:r||[],onChange:i});default:return(0,t.jsx)(d.TextControl,{__nextHasNoMarginBottom:!0,label:e.label,value:r??"",onChange:i})}}function j({label:e,choices:n,value:r,onChange:i}){const o=Object.fromEntries(Object.entries(n).map(([e,n])=>[n,e]));return(0,t.jsx)(d.FormTokenField,{__nextHasNoMarginBottom:!0,__experimentalExpandOnFocus:!0,label:e,value:r.map(e=>n[e]||e),suggestions:Object.values(n),onChange:e=>i(e.map(e=>o[e]).filter(Boolean))})}function m({label:e,value:i,onChange:o}){const[a,s]=(0,n.useState)({});return(0,n.useEffect)(()=>{c()({path:"/wp/v2/product_cat?per_page=100&_fields=id,name"}).then(e=>s(Object.fromEntries(e.map(e=>[e.id,e.name])))).catch(()=>s({}))},[]),(0,t.jsx)(j,{label:e||(0,r.__)("Categories","joinery-core"),choices:a,value:i.map(String),onChange:e=>o(e.map(Number))})}function y({label:e,help:r,value:i,onChange:o}){const[a,s]=(0,n.useState)([]),l=e=>e.map(e=>({value:String(e.id),label:e.name})),u=e=>c()({path:`/wc/v3/products?status=publish&type=simple&per_page=20&_fields=id,name&search=${encodeURIComponent(e||"")}`}).then(e=>s(n=>[...n.filter(e=>e.value===String(i)),...l(e).filter(e=>e.value!==String(i))])).catch(()=>{});return(0,n.useEffect)(()=>{i&&c()({path:`/wc/v3/products/${Number(i)}?_fields=id,name`}).then(e=>s(n=>[...l([e]),...n])).catch(()=>{}),u("")},[]),(0,t.jsx)(d.ComboboxControl,{__nextHasNoMarginBottom:!0,label:e,help:r,value:i?String(i):null,options:a,onFilterValueChange:u,onChange:e=>o(e?Number(e):"")})}const p="joinery-more-rule-types-hidden",_=()=>{try{return"1"===window.localStorage.getItem(p)}catch{return!1}};function x({schema:e}){const[i,o]=(0,n.useState)(_),a=[...e.conditions,...e.actions].map(e=>e.id),l=[{prefix:"dp_",name:(0,r.__)("Dynamic Pricing & Discounts","joinery-core"),text:(0,r.__)("Quantity tiers, buy X get Y, free gifts, fixed prices, weekdays and first orders.","joinery-core"),url:"https://www.joinerywp.com/plugins/dynamic-pricing"},{prefix:"b2b_",name:(0,r.__)("B2B & Wholesale","joinery-core"),text:(0,r.__)("Rules for trade tiers such as Wholesale or Gold.","joinery-core"),url:"https://www.joinerywp.com/plugins/b2b-wholesale"},{prefix:"gc_",name:(0,r.__)("Gift Cards & Store Credit","joinery-core"),text:(0,r.__)("Give store credit after an order.","joinery-core"),url:"https://www.joinerywp.com/plugins/gift-cards"},{prefix:"pr_",name:(0,r.__)("Points & Rewards","joinery-core"),text:(0,r.__)("Give bonus points after an order.","joinery-core"),url:"https://www.joinerywp.com/plugins/points-rewards"}].filter(e=>!a.some(n=>n.startsWith(e.prefix)));return i||0===l.length?null:(0,t.jsxs)(s,{className:"joinery-more",children:[(0,t.jsx)("p",{className:"joinery-eyebrow",children:(0,r.__)("More rule types","joinery-core")}),(0,t.jsx)("p",{className:"joinery-more__intro",children:(0,r.__)("Paid Joinery plugins add their conditions and actions to this screen.","joinery-core")}),(0,t.jsx)("ul",{children:l.map(e=>(0,t.jsxs)("li",{children:[(0,t.jsx)(d.ExternalLink,{href:e.url,children:e.name}),(0,t.jsx)("span",{children:e.text})]},e.prefix))}),(0,t.jsx)(d.Button,{variant:"link",onClick:()=>{try{window.localStorage.setItem(p,"1")}catch{}o(!0)},children:(0,r.__)("Hide this","joinery-core")})]})}function g(){const[e,i]=(0,n.useState)(null),[o,l]=(0,n.useState)([]),[u,h]=(0,n.useState)(null),[j,m]=(0,n.useState)(""),y=()=>Promise.all([c()({path:"/joinery/v1/schema"}),c()({path:"/joinery/v1/rules"})]).then(([e,n])=>{i(e),l(n),h(e=>e||n[0]||{id:0,name:"",active:!0,priority:10,exclusive:!1,conditions:[],actions:[]})}).catch(e=>m(e.message));return(0,n.useEffect)(()=>{y()},[]),j?(0,t.jsx)(d.Notice,{status:"error",isDismissible:!1,children:j}):e?(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(a,{eyebrow:(0,r.__)("Rules","joinery-core"),title:(0,r.__)("Every price, discount and reward rule.","joinery-core"),description:(0,r.__)("Rules here are shared by all your Joinery plugins.","joinery-core"),action:(0,t.jsx)(d.Button,{variant:"primary",onClick:()=>h({id:0,name:"",active:!0,priority:10,exclusive:!1,conditions:[],actions:[]}),children:(0,r.__)("Add rule","joinery-core")})}),(0,t.jsxs)("div",{className:"joinery-rules",children:[(0,t.jsxs)("div",{className:"joinery-rules__side",children:[(0,t.jsxs)(s,{className:"joinery-rules__list",children:[(0,t.jsxs)("p",{className:"joinery-eyebrow",children:[(0,r.__)("All rules","joinery-core")," (",o.length,")"]}),0===o.length&&(0,t.jsx)("p",{children:(0,r.__)("No rules yet. Add your first one.","joinery-core")}),o.map(e=>(0,t.jsxs)("button",{type:"button",className:"joinery-rules__item","aria-current":u?.id===e.id?"true":void 0,onClick:()=>h(e),children:[(0,t.jsx)("strong",{children:e.name}),(0,t.jsx)("span",{children:e.active?(0,r.__)("Active","joinery-core"):(0,r.__)("Off","joinery-core")})]},e.id))]}),(0,t.jsx)(x,{schema:e})]}),u&&(0,t.jsx)(b,{rule:u,schema:e,onSaved:e=>{h(e),y()}},u.id||"new")]})]}):(0,t.jsx)(d.Spinner,{})}function b({rule:e,schema:i,onSaved:o}){const[a,l]=(0,n.useState)(e),[u,h]=(0,n.useState)(!1),[j,m]=(0,n.useState)(null),y=e=>l(n=>({...n,...e}));return(0,t.jsxs)(s,{className:"joinery-editor",children:[(0,t.jsxs)("div",{className:"joinery-editor__head",children:[(0,t.jsx)(d.TextControl,{__nextHasNoMarginBottom:!0,label:(0,r.__)("Rule name","joinery-core"),value:a.name,onChange:e=>y({name:e})}),(0,t.jsx)(d.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,r.__)("Rule is active","joinery-core"),checked:a.active,onChange:e=>y({active:e})})]}),(0,t.jsx)(v,{title:(0,r.__)("When all of these are true","joinery-core"),addLabel:(0,r.__)("+ Add condition","joinery-core"),parts:a.conditions,available:i.conditions,schema:i,onChange:e=>y({conditions:e})}),(0,t.jsx)(v,{title:(0,r.__)("Then do this","joinery-core"),addLabel:(0,r.__)("+ Add action","joinery-core"),parts:a.actions,available:i.actions,schema:i,onChange:e=>y({actions:e})}),(0,t.jsxs)("div",{className:"joinery-editor__foot",children:[(0,t.jsx)(d.TextControl,{__nextHasNoMarginBottom:!0,type:"number",label:(0,r.__)("Priority (lower runs first)","joinery-core"),value:String(a.priority),onChange:e=>y({priority:Number(e)||0})}),(0,t.jsx)(d.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,r.__)("Don't combine with other rules","joinery-core"),checked:a.exclusive,onChange:e=>y({exclusive:e})}),(0,t.jsx)(d.Button,{variant:"primary",onClick:()=>{h(!0),m(null),c()({path:a.id?`/joinery/v1/rules/${a.id}`:"/joinery/v1/rules",method:a.id?"PUT":"POST",data:a}).then(e=>{m({status:"success",text:(0,r.__)("Rule saved.","joinery-core")}),o(e)}).catch(e=>m({status:"error",text:e.message})).finally(()=>h(!1))},isBusy:u,disabled:u,children:(0,r.__)("Save rule","joinery-core")})]}),j&&(0,t.jsx)(d.Notice,{status:j.status,onRemove:()=>m(null),children:j.text})]})}function v({title:e,addLabel:n,parts:i,available:o,schema:a,onChange:s}){const l=(e,n)=>s(i.map((r,t)=>t===e?n:r));return(0,t.jsxs)("div",{className:"joinery-parts",children:[(0,t.jsx)("p",{className:"joinery-eyebrow",children:e}),i.map((e,n)=>{const c=o.find(n=>n.id===e.type);return(0,t.jsxs)("div",{className:"joinery-part",children:[(0,t.jsx)(d.SelectControl,{__nextHasNoMarginBottom:!0,label:(0,r.__)("Type","joinery-core"),value:e.type,options:o.map(e=>({value:e.id,label:e.label})),onChange:e=>l(n,{type:e,settings:{}})}),c?.fields.map(r=>(0,t.jsx)(h,{field:r,schema:a,value:e.settings[r.key]??r.default,onChange:t=>l(n,{...e,settings:{...e.settings,[r.key]:t}})},r.key)),(0,t.jsx)(d.Button,{variant:"link",isDestructive:!0,onClick:()=>(e=>s(i.filter((n,r)=>r!==e)))(n),children:(0,r.__)("Remove","joinery-core")})]},n)}),(0,t.jsx)(d.Button,{variant:"secondary",onClick:()=>o[0]&&s([...i,{type:o[0].id,settings:{}}]),children:n})]})}const f=[{id:"rules",label:(0,r.__)("Rules","joinery-core"),render:()=>(0,t.jsx)(g,{})}];window.joinery=window.joinery||{},window.joinery.registerTab=e=>f.push(e),window.joinery.ui={Card:s,PageIntro:a,Tag:function({tone:e="neutral",children:n}){return(0,t.jsx)("span",{className:`joinery-tag joinery-tag--${e}`,children:n})}},document.addEventListener("DOMContentLoaded",()=>{const e=document.getElementById("joinery-admin-root");e&&(0,n.createRoot)(e).render((0,t.jsx)(o,{tabs:f}))})})();
  • joinery-core/trunk/changelog.txt

    r3767875 r3769397  
    11*** Joinery Core Changelog ***
     2
     32026-10-27 - version 0.4.0
     4* New: A “More rule types” panel on Joinery → Rules links to the paid plugins that add rule types. It lists only plugins whose rule types aren’t registered, and “Hide this” keeps it hidden in that browser.
    25
    362026-10-19 - version 0.3.0
  • joinery-core/trunk/joinery-core.php

    r3767875 r3769397  
    33 * Plugin Name:          Joinery Core
    44 * Description:          The shared core for Joinery plugins: one rules engine, one pricing pipeline and one customer wallet. Includes free discount rules.
    5  * Version:              0.3.0
     5 * Version:              0.4.0
    66 * Requires at least:    6.5
    77 * Requires PHP:         8.1
    … …  
    2222defined( 'ABSPATH' ) || exit;
    2323
    24 define( 'JOINERY_CORE_VERSION', '0.3.0' );
     24define( 'JOINERY_CORE_VERSION', '0.4.0' );
    2525define( 'JOINERY_CORE_FILE', __FILE__ );
    2626
  • joinery-core/trunk/readme.txt

    r3769298 r3769397  
    55Tested up to: 7.1
    66Requires PHP: 8.1
    7 Stable tag: 0.3.0
     7Stable tag: 0.4.0
    88License: GPLv2 or later
    99License URI: https://www.gnu.org/licenses/gpl-2.0.html
    … …  
    4343= Do I need a paid plugin? =
    4444
    45 No. Joinery Core works on its own. Paid Joinery plugins add more rule types, such as tiered pricing, wholesale accounts and gift cards, to the same rules screen.
     45No. Joinery Core works on its own. Paid Joinery plugins add more rule types, such as tiered pricing, wholesale accounts and gift cards, to the same rules screen. A small “More rule types” panel there lists the ones you don’t have; you can hide it.
    4646
    4747= Does it slow down my store? =
    … …  
    6262== Changelog ==
    6363
     64= 0.4.0 =
     65* New: A small “More rule types” panel on Joinery → Rules lists the paid Joinery plugins that add rule types. It only shows plugins you don’t have, and you can hide it.
     66
    6467= 0.3.0 =
    6568* New: Rules can tell whether the customer has ordered before, for first-order offers.
Note: See TracChangeset for help on using the changeset viewer.