Rendering AshPaperTrails

Is there a good way to render the change history for a resource recorded by AshPaperTrail?
Solution:
In my Member ressource, I have: ```paper_trail do primary_key_type :uuid only_when_changed? true change_tracking_mode :changes_only...
Jump to solution
10 Replies
allenwyma
allenwyma2w ago
@AngyL75 has experience with this.
ZachDaniel
ZachDaniel2w ago
Over an API? Or just in your app? In LV?
Robert Graff
Robert Graff2w ago
@allenwyma I don't know of any good public examples, but if you find one let me know.
aidalgol
aidalgolOP2w ago
Oh, sorry. I meant in LiveViews.
ZachDaniel
ZachDaniel2w ago
In that case, you can interact with the versions resource directly, and/or load the versions relationship on the record (I can't remember if thats enabled by default or not, but you can see the DSL options there)
AngyL75
AngyL752w ago
Hi @aidalgol, I am using paper_trail to record history of a resource. In my liveview I show the history of modification. For example, I have a "Member" ressource to connect a User to an Organization. and I can see the history of modication of his status (see screenshot, also, this is temporary html, it needs UX improvement)
No description
Solution
AngyL75
AngyL752w ago
In my Member ressource, I have:
paper_trail do
primary_key_type :uuid
only_when_changed? true
change_tracking_mode :changes_only
store_action_name? true
store_action_inputs? true
ignore_attributes [:inserted_at, :updated_at]
ignore_actions [:destroy]
# for multitenancy
attributes_as_attributes [:organization_id]
belongs_to_actor :actor, CauseBeacon.Accounts.User, public?: false
end
paper_trail do
primary_key_type :uuid
only_when_changed? true
change_tracking_mode :changes_only
store_action_name? true
store_action_inputs? true
ignore_attributes [:inserted_at, :updated_at]
ignore_actions [:destroy]
# for multitenancy
attributes_as_attributes [:organization_id]
belongs_to_actor :actor, CauseBeacon.Accounts.User, public?: false
end
Note: - in :actor the person who modify the resource is stored - :store_action_name is very usefull to know which action has been called The status of my member is an Enum:
use Ash.Type.Enum,
values: [
invited: gettext("Invited"),
validated: gettext("Pending (Connection)"),
connected: gettext("Pending (Validation)"),
waiting_list: gettext("Waiting List"),
activated: gettext("Active"),
left: gettext("Left")
]
use Ash.Type.Enum,
values: [
invited: gettext("Invited"),
validated: gettext("Pending (Connection)"),
connected: gettext("Pending (Validation)"),
waiting_list: gettext("Waiting List"),
activated: gettext("Active"),
left: gettext("Left")
]
Now, when I load my member data in my liveview, I do:
member =
Ash.load!(
assigns.member,
[
:connections,
paper_trail_versions:
CauseBeacon.Organizations.Member.Version
|> Ash.Query.load(:actor)
],
actor: assigns.current_user
)
member =
Ash.load!(
assigns.member,
[
:connections,
paper_trail_versions:
CauseBeacon.Organizations.Member.Version
|> Ash.Query.load(:actor)
],
actor: assigns.current_user
)
and after I am doing the processing of the version like:
membership_history =
member.paper_trail_versions
|> Enum.filter(fn version -> Map.has_key?(version.changes, "status") end)
|> Enum.map(....)
membership_history =
member.paper_trail_versions
|> Enum.filter(fn version -> Map.has_key?(version.changes, "status") end)
|> Enum.map(....)
AngyL75
AngyL752w ago
Hi @aidalgol does it reply to your question ?
aidalgol
aidalgolOP2w ago
Mostly, yes, thank you! What does your LiveView (or plain HTML) template look like?
AngyL75
AngyL752w ago
Hi @aidalgol the code related to the screenshot above is
<div :if={@membership_history != []} class="mt-2">
<h1>{gettext("History of membership")}</h1>
<ul class="list-disc pl-5">
<li :for={item <- @membership_history}>
<span>
{CauseBeacon.Cldr.DateTime.to_string!(
item.version_inserted_at,
format: :short
)}: <MemberBadgeStatus.badge_status status={item.status} />
{item.message}
</span>
</li>
</ul>
</div>
<div :if={@membership_history != []} class="mt-2">
<h1>{gettext("History of membership")}</h1>
<ul class="list-disc pl-5">
<li :for={item <- @membership_history}>
<span>
{CauseBeacon.Cldr.DateTime.to_string!(
item.version_inserted_at,
format: :short
)}: <MemberBadgeStatus.badge_status status={item.status} />
{item.message}
</span>
</li>
</ul>
</div>

Did you find this page helpful?