This document describes the first-pass data model for Kelompok.
The model should combine structured SQL tables with JSONB fields for dynamic source data.
Stable fields:
idslugnamelegal_namedescriptionhistorycountryregioncitywebsite_urlofficial_emaillogo_file_idbanner_file_idclaim_statusclaimed_by_user_idclaimed_atcreated_atupdated_at
Dynamic fields:
profile_data jsonbsource_data jsonbsdgs_data jsonbimpact_data jsonb
Notes:
profile_datastores dynamic public profile fields.source_datastores imported or enriched external source results.sdgs_datastores detected SDGS tags, confidence, and evidence.impact_datastores custom impact reporting fields.- Admin create/edit supports the stable text fields plus
profile_data,source_data,sdgs_data, andimpact_data. - File-backed
logo_file_id/banner_file_idand directclaimed_by_user_id/claimed_atediting are intentionally deferred until the file picker and claim-review workflow own those fields end to end.
Tracks where data came from.
Fields:
idsource_typesource_urlsource_nameexternal_idraw_payload jsonbhashfirst_seen_atlast_seen_atcreated_at
Source records allow imported or enriched data to be auditable and refreshed without losing evidence.
Connects organizations to source records.
Fields:
organization_idsource_record_idmatch_statusconfidencecreated_at
Represents parent/child and related-organization graph edges without forcing each organization into one parent column.
Fields:
idparent_organization_idchild_organization_idrelationship_typelabelstatusstarted_atended_atmetadata jsonbcreated_atupdated_at
Relationship types:
structural_parentfor formal hierarchyautonomous_bodyfor autonomous bodies under a broader movementaffiliated_withfor affiliation without direct managementnetwork_memberfor federation or network membershiprelatedfor loose public relationships
Rules:
- Parent and child cannot be the same organization.
- Duplicate
parent + child + relationship_typerows are rejected. - Relationship metadata is kept out of public responses by default.
- Relationship records do not grant inherited edit permissions in the first slice.
- Relationship update audits are written for both current and previous parent/child organization scopes so removed organizations keep historical visibility.
Tracks organization claim attempts.
Fields:
idorganization_iduser_idmethodtargetstatusevidence jsonbreviewed_by_user_idreviewed_atcreated_atupdated_at
Claim methods:
official_emailinstagrammanual_review
Fields:
idnameemailpassword_hashroleemail_verified_atcreated_atupdated_at
Roles:
superadminowneradminmemberviewer
The global users.role is primarily viewer or superadmin in the current
alpha. Organization-scoped authority lives in organization_user_roles.role,
where owner and admin are the only roles allowed to manage organization
data or review related claims.
Represents people related to an organization.
Fields:
idorganization_idnamepositionbioemailphonesocial_links jsonbstart_dateend_datecreated_atupdated_at
Use cases:
- Chairperson
- Board member
- Founder
- Public contact
- Volunteer coordinator
Stores SDGS detection results.
Fields:
idorganization_idsdg_codeconfidenceevidence_textevidence_source_record_iddetected_bycreated_at
This makes SDGS data queryable while still allowing full dynamic evidence in JSONB.
Fields:
idorganization_idtitlesummaryreport_period_startreport_period_endsdgs jsonbmetrics jsonbstatuspublished_atcreated_atupdated_at
Impact reports are public when published.
Represents organization-owned articles, news, announcements, and public updates.
Fields:
idorganization_idauthor_user_idslugtitlesummarycontentcover_file_idstatuspublished_atcreated_atupdated_at
Dynamic fields:
post_data jsonbseo_data jsonb
Post statuses:
draftpublishedarchived
Fields:
idslugnamedescriptioncreated_atupdated_at
Fields:
idslugnamecreated_atupdated_at
Fields:
post_idtag_id
Fields:
idorganization_idslugtitledescriptionstart_atend_attimezonelocation_typelocation_namelocation_addressonline_urlsource_record_idstatuscreated_atupdated_at
Dynamic fields:
event_data jsonbsource_data jsonb
Fields:
idevent_idnamedescriptionprice_amountcurrencyquotasales_start_atsales_end_atcreated_atupdated_at
The first version can support free and paid tickets in the model, even if payment comes later.
Fields:
idevent_idticket_type_idattendee_nameattendee_emailattendee_phonestatusmetadata jsonbcreated_atupdated_at
Fields:
idorganization_idslugtitledescriptiongoal_amountcurrencystart_atend_atstatuscreated_atupdated_at
Fields:
idcampaign_idtitlesummaryamount_usedcurrencyreport_data jsonbpublished_atcreated_atupdated_at
Donation reports should make fund usage visible to the public.
Fields:
idactor_user_identity_typeentity_idactionbefore jsonbafter jsonbcreated_at
Important for claims, profile edits, source-data merges, and moderation.
- Stable, repeated fields should be columns
- Dynamic provider-specific fields should be JSONB
- Never overwrite source evidence
- Manual edits should be stored separately from raw source data
- Claims and public data changes should be auditable
- Public pages should be renderable from normalized data without requiring raw source payloads