ee1c7959 Changed the custom field modal to open a field to members with a switch
ref https://linear.app/ghost/issue/BER-3794/members-manage-their-own-custom-fields-in-their-account
A publisher deciding what members may do with a custom field chose between
three levels in a select: staff only, members can view, members can edit. In
practice the decision is one question, whether members maintain this field
themselves, and the three-way control asked more than that while hiding the
one fact that matters when the answer changes: how many people will see it.
The modal now asks it as the newsletter modal asks its own opt-in question: a
switch labelled "Visible to members", with a sentence beneath saying how many
members can see and update the field from their Portal account settings, and,
when a field that was staff-only is opened, that anything already recorded
becomes visible to them. The list says so in the row's subtitle, after the
field's type, the way the staff list carries a fact about a person under
their name; on active fields only, since an archived field is closed whatever
its setting says.
The view-only level stays in the API. A field already at it shows as open,
says that members can see but not change it, saves untouched when only its
name changes, and returns to view-only if the switch is turned off and on
again, rather than being upgraded to editable on the way. c2a9be31 Added a country picker to address fields in Portal and Admin
ref https://linear.app/ghost/issue/BER-3804/improve-address-collection
The country part of an address was a two-letter code typed by hand, in the
member's Portal account settings and in the address editor on a member's
record. Both surfaces refused anything that was not two letters, and neither
told the person which two, so "Germany" and "DEU" were both dead ends.
One catalogue now lists every country an address may name, as ISO 3166-1
codes in the shared field-types package, since an address is source-agnostic
and any of Ghost's collection points may write one. Names come from the
browser's own locale data in both apps, in the site's language for a member
and in English for staff, so the same code reads the same everywhere and the
two Congos read the way address forms show them. Portal offers the list as a
native select styled like its other inputs; Admin offers it as the searchable
picker the tier checkout card and the language setting already use, and the
tier card keeps its own list of where Stripe ships. Address summaries on a
member's record, in the members list and in filter chips read the country by
name rather than by code; exports and the API keep the code.
A stored code the catalogue does not hold, such as a Stripe-only shipping
code from checkout or an imported value, is shown as an extra option rather
than blanked, so what a record holds is always visible and can be replaced. 113076c3 Added a country picker to address fields in Portal and Admin
ref https://linear.app/ghost/issue/BER-3804/improve-address-collection
The country part of an address was a two-letter code typed by hand, in the
member's Portal account settings and in the address editor on a member's
record. Both surfaces refused anything that was not two letters, and neither
told the person which two, so "Germany" and "DEU" were both dead ends.
One catalogue now lists every country an address may name, as ISO 3166-1
codes in the shared field-types package, since an address is source-agnostic
and any of Ghost's collection points may write one. Names come from the
browser's own locale data in both apps, in the site's language for a member
and in English for staff, so the same code reads the same everywhere and the
two Congos read the way address forms show them. Portal offers the list as a
native select styled like its other inputs; Admin offers it as the searchable
picker the tier checkout card and the language setting already use, and the
tier card keeps its own list of where Stripe ships. Address summaries on a
member's record, in the members list and in filter chips read the country by
name rather than by code; exports and the API keep the code.
A stored code the catalogue does not hold, such as a Stripe-only shipping
code from checkout or an imported value, is shown as an extra option rather
than blanked, so what a record holds is always visible and can be replaced. 901b27c1 Added a country picker to address fields in Portal and Admin
ref https://linear.app/ghost/issue/BER-3804/improve-address-collection
The country part of an address was a two-letter code typed by hand, in the
member's Portal account settings and in the address editor on a member's
record. Both surfaces refused anything that was not two letters, and neither
told the person which two, so "Germany" and "DEU" were both dead ends.
One catalogue now lists every country an address may name, as ISO 3166-1
codes in the shared field-types package, since an address is source-agnostic
and any of Ghost's collection points may write one. Names come from the
browser's own locale data in both apps, in the site's language for a member
and in English for staff, so the same code reads the same everywhere and the
two Congos read the way address forms show them. Portal offers the list as a
native select styled like its other inputs; Admin offers it as the searchable
picker the tier checkout card and the language setting already use, and the
tier card keeps its own list of where Stripe ships. Address summaries on a
member's record, in the members list and in filter chips read the country by
name rather than by code; exports and the API keep the code.
A stored code the catalogue does not hold, such as a Stripe-only shipping
code from checkout or an imported value, is shown as an extra option rather
than blanked, so what a record holds is always visible and can be replaced. b788e0c0 Added a country picker to address fields in Portal and Admin
ref https://linear.app/ghost/issue/BER-3804/improve-address-collection
The country part of an address was a two-letter code typed by hand, in the
member's Portal account settings and in the address editor on a member's
record. Both surfaces refused anything that was not two letters, and neither
told the person which two, so "Germany" and "DEU" were both dead ends.
One catalogue now lists every country an address may name, as ISO 3166-1
codes in the shared field-types package, since an address is source-agnostic
and any of Ghost's collection points may write one. Names come from the
browser's own locale data in both apps, in the site's language for a member
and in English for staff, so the same code reads the same everywhere and the
two Congos read the way address forms show them. Portal offers the list as a
native select styled like its other inputs; Admin offers it as the searchable
picker the tier checkout card and the language setting already use, and the
tier card keeps its own list of where Stripe ships. Address summaries on a
member's record, in the members list and in filter chips read the country by
name rather than by code; exports and the API keep the code.
A stored code the catalogue does not hold, such as a Stripe-only shipping
code from checkout or an imported value, is shown as an extra option rather
than blanked, so what a record holds is always visible and can be replaced. 4d9cea19 Changed the custom field modal to open a field to members with a switch
ref https://linear.app/ghost/issue/BER-3794/members-manage-their-own-custom-fields-in-their-account
A publisher deciding what members may do with a custom field chose between
three levels in a select: staff only, members can view, members can edit. In
practice the decision is one question, whether members maintain this field
themselves, and the three-way control asked more than that while hiding the
one fact that matters when the answer changes: how many people will see it.
The modal now asks it as the newsletter modal asks its own opt-in question: a
switch labelled "Visible to members", with a sentence beneath saying how many
members can see and update the field from their Portal account settings, and,
when a field that was staff-only is opened, that anything already recorded
becomes visible to them. The list marks an open field with a badge, on active
fields only, since an archived field is closed whatever its setting says.
The view-only level stays in the API. A field already at it shows as open,
says that members can see but not change it, saves untouched when only its
name changes, and returns to view-only if the switch is turned off and on
again, rather than being upgraded to editable on the way. 16e7907a Changed the custom field modal to open a field to members with a switch
ref https://linear.app/ghost/issue/BER-3794/members-manage-their-own-custom-fields-in-their-account
A publisher deciding what members may do with a custom field chose between
three levels in a select: staff only, members can view, members can edit. In
practice the decision is one question, whether members maintain this field
themselves, and the three-way control asked more than that while hiding the
one fact that matters when the answer changes: how many people will see it.
The modal now asks it as the newsletter modal asks its own opt-in question: a
switch labelled "Visible to members", with a sentence beneath saying how many
members can see and update the field from their Portal account settings, and,
when a field that was staff-only is opened, that anything already recorded
becomes visible to them. The list marks an open field with a badge, on active
fields only, since an archived field is closed whatever its setting says.
The view-only level stays in the API. A field already at it shows as open,
says that members can see but not change it, saves untouched when only its
name changes, and returns to view-only if the switch is turned off and on
again, rather than being upgraded to editable on the way. 137ea30f Added a country picker to address fields in Portal and Admin
ref https://linear.app/ghost/issue/BER-3804/improve-address-collection
The country part of an address was a two-letter code typed by hand, in the
member's Portal account settings and in the address editor on a member's
record. Both surfaces refused anything that was not two letters, and neither
told the person which two, so "Germany" and "DEU" were both dead ends.
One catalogue now lists every country an address may name, as ISO 3166-1
codes in the shared field-types package, since an address is source-agnostic
and any of Ghost's collection points may write one. Names come from the
browser's own locale data in both apps, in the site's language for a member
and in English for staff, so the same code reads the same everywhere and the
two Congos read the way address forms show them. Portal offers the list as a
native select styled like its other inputs; Admin offers it as the searchable
picker the tier checkout card and the language setting already use, and the
tier card keeps its own list of where Stripe ships. Address summaries on a
member's record, in the members list and in filter chips read the country by
name rather than by code; exports and the API keep the code.
A stored code the catalogue does not hold, such as a Stripe-only shipping
code from checkout or an imported value, is shown as an extra option rather
than blanked, so what a record holds is always visible and can be replaced. 22c59dd4 Added a members list column for each custom field filtered on
A filter told a publisher who matched but not what they said, so reading
the data they had just filtered on meant opening members one at a time.
Labels, tiers and the subscription filters already append a column for
that reason; custom fields are the case where it matters most, since a
field exists precisely because its value differs member to member. Every
custom field filtered on now brings its own column, whatever the operator,
the way Label does.
The column layer already lets a field declare a column and how to read it.
What is new is that a field whose key is a pattern stands for many columns
rather than one, so `custom_fields.:key` resolves its column per instance
from the matched key, hydrated from the fields the API loaded -- the same
hydration the filter list already does for newsletters, tiers and offers.
A name it cannot resolve is a column it does not show, which is also what
the flag being off looks like from there. Asking the API for the values
does not wait on those names, so the list does not fetch them twice, and
the fetch that names them waits for a filter rather than riding every
visit to the list.
Rendering a value as one line was the member detail screen's, and is now
the framework's user-type catalog, next to the part labels and CSV column
names it already owns. A scalar reads as it stands and a composite joins
its parts the way that type reads; the detail screen renders through the
same function, so the two cannot drift, and the catalog is total over the
field types so a new composite cannot arrive without a line to read as.
Values a publisher collected have no length a column can be sized to, so
a dynamic cell carries its full text as a native title. That is a fallback
for the edge, not a tooltip component per row across a virtualised list. 22c59dd4 Added a members list column for each custom field filtered on
A filter told a publisher who matched but not what they said, so reading
the data they had just filtered on meant opening members one at a time.
Labels, tiers and the subscription filters already append a column for
that reason; custom fields are the case where it matters most, since a
field exists precisely because its value differs member to member. Every
custom field filtered on now brings its own column, whatever the operator,
the way Label does.
The column layer already lets a field declare a column and how to read it.
What is new is that a field whose key is a pattern stands for many columns
rather than one, so `custom_fields.:key` resolves its column per instance
from the matched key, hydrated from the fields the API loaded -- the same
hydration the filter list already does for newsletters, tiers and offers.
A name it cannot resolve is a column it does not show, which is also what
the flag being off looks like from there. Asking the API for the values
does not wait on those names, so the list does not fetch them twice, and
the fetch that names them waits for a filter rather than riding every
visit to the list.
Rendering a value as one line was the member detail screen's, and is now
the framework's user-type catalog, next to the part labels and CSV column
names it already owns. A scalar reads as it stands and a composite joins
its parts the way that type reads; the detail screen renders through the
same function, so the two cannot drift, and the catalog is total over the
field types so a new composite cannot arrive without a line to read as.
Values a publisher collected have no length a column can be sized to, so
a dynamic cell carries its full text as a native title. That is a fallback
for the edge, not a tooltip component per row across a virtualised list. 5f492b9b Added a members list column for each custom field filtered on
A filter told a publisher who matched but not what they said, so reading
the data they had just filtered on meant opening members one at a time.
Labels, tiers and the subscription filters already append a column for
that reason; custom fields are the case where it matters most, since a
field exists precisely because its value differs member to member. Every
custom field filtered on now brings its own column, whatever the operator,
the way Label does.
The column layer already lets a field declare a column and how to read it.
What is new is that a field whose key is a pattern stands for many columns
rather than one, so `custom_fields.:key` resolves its column per instance
from the matched key, hydrated from the fields the API loaded -- the same
hydration the filter list already does for newsletters, tiers and offers.
A name it cannot resolve is a column it does not show, which is also what
the flag being off looks like from there. Asking the API for the values
does not wait on those names, so the list does not fetch them twice, and
the fetch that names them waits for a filter rather than riding every
visit to the list.
Rendering a value as one line was the member detail screen's, and is now
the framework's user-type catalog, next to the part labels and CSV column
names it already owns. A scalar reads as it stands and a composite joins
its parts the way that type reads; the detail screen renders through the
same function, so the two cannot drift, and the catalog is total over the
field types so a new composite cannot arrive without a line to read as.
Values a publisher collected have no length a column can be sized to, so
a dynamic cell carries its full text as a native title. That is a fallback
for the edge, not a tooltip component per row across a virtualised list. ed8c9192 Added a members list column for each custom field filtered on
A filter told a publisher who matched but not what they said, so reading
the data they had just filtered on meant opening members one at a time.
Labels, tiers and the subscription filters already append a column for
that reason; custom fields are the case where it matters most, since a
field exists precisely because its value differs member to member. Every
custom field filtered on now brings its own column, whatever the operator,
the way Label does.
The column layer already lets a field declare a column and how to read it.
What is new is that a field whose key is a pattern stands for many columns
rather than one, so `custom_fields.:key` resolves its column per instance
from the matched key, hydrated from the fields the API loaded -- the same
hydration the filter list already does for newsletters, tiers and offers.
A name it cannot resolve is a column it does not show, which is also what
the flag being off looks like from there. Asking the API for the values
does not wait on those names, so the list does not fetch them twice, and
the fetch that names them waits for a filter rather than riding every
visit to the list.
Rendering a value as one line was the member detail screen's, and is now
the framework's user-type catalog, next to the part labels and CSV column
names it already owns. A scalar reads as it stands and a composite joins
its parts the way that type reads; the detail screen renders through the
same function, so the two cannot drift, and the catalog is total over the
field types so a new composite cannot arrive without a line to read as.
Values a publisher collected have no length a column can be sized to, so
a dynamic cell carries its full text as a native title. That is a fallback
for the edge, not a tooltip component per row across a virtualised list.