diff --git a/api/routes.go b/api/routes.go index ac71fadf..49f5f521 100644 --- a/api/routes.go +++ b/api/routes.go @@ -27,6 +27,7 @@ func AddRoutes(r chi.Router) { r.Method("GET", "/signal", authenticatedHandlerJSON(listSignal)) r.Method("GET", "/trap-data", auth.NewEnsureAuth(apiTrapData)) r.Method("GET", "/tile/{z}/{y}/{x}", auth.NewEnsureAuth(getTile)) + r.Method("GET", "/user", authenticatedHandlerJSON(getUser)) // Unauthenticated endpoints r.Get("/district", apiGetDistrict) diff --git a/api/user.go b/api/user.go new file mode 100644 index 00000000..0326de19 --- /dev/null +++ b/api/user.go @@ -0,0 +1,18 @@ +package api + +import ( + "context" + "net/http" + + nhttp "github.com/Gleipnir-Technology/nidus-sync/http" + "github.com/Gleipnir-Technology/nidus-sync/platform" +) + +func getUser(ctx context.Context, r *http.Request, user platform.User, query queryParams) (*platform.User, *nhttp.ErrorWithStatus) { + counts, err := platform.NotificationCountsForUser(ctx, user) + if err != nil { + return nil, nhttp.NewError("get notifications: %w", err) + } + user.NotificationCounts = *counts + return &user, nil +} diff --git a/html/static/js/alpine-3.15.8-min.js b/html/static/js/alpine-3.15.8-min.js new file mode 100644 index 00000000..182d70cc --- /dev/null +++ b/html/static/js/alpine-3.15.8-min.js @@ -0,0 +1,3018 @@ +(() => { + var ie = !1, + oe = !1, + Y = [], + se = -1, + ae = !1; + function We(t) { + Bn(t); + } + function Ge() { + ae = !0; + } + function Je() { + ((ae = !1), Xe()); + } + function Bn(t) { + (Y.includes(t) || Y.push(t), Xe()); + } + function Ye(t) { + let e = Y.indexOf(t); + e !== -1 && e > se && Y.splice(e, 1); + } + function Xe() { + if (!oe && !ie) { + if (ae) return; + ((ie = !0), queueMicrotask(zn)); + } + } + function zn() { + ((ie = !1), (oe = !0)); + for (let t = 0; t < Y.length; t++) (Y[t](), (se = t)); + ((Y.length = 0), (se = -1), (oe = !1)); + } + var M, + D, + z, + le, + ce = !0; + function Ze(t) { + ((ce = !1), t(), (ce = !0)); + } + function Qe(t) { + ((M = t.reactive), + (z = t.release), + (D = (e) => + t.effect(e, { + scheduler: (r) => { + ce ? We(r) : r(); + }, + })), + (le = t.raw)); + } + function ue(t) { + D = t; + } + function tr(t) { + let e = () => {}; + return [ + (n) => { + let i = D(n); + return ( + t._x_effects || + ((t._x_effects = new Set()), + (t._x_runEffects = () => { + t._x_effects.forEach((o) => o()); + })), + t._x_effects.add(i), + (e = () => { + i !== void 0 && (t._x_effects.delete(i), z(i)); + }), + i + ); + }, + () => { + e(); + }, + ]; + } + function Ot(t, e) { + let r = !0, + n, + i = D(() => { + let o = t(); + if ((JSON.stringify(o), !r && (typeof o == "object" || o !== n))) { + let s = n; + queueMicrotask(() => { + e(o, s); + }); + } + ((n = o), (r = !1)); + }); + return () => z(i); + } + async function er(t) { + Ge(); + try { + (await t(), await Promise.resolve()); + } finally { + Je(); + } + } + var rr = [], + nr = [], + ir = []; + function or(t) { + ir.push(t); + } + function it(t, e) { + typeof e == "function" + ? (t._x_cleanups || (t._x_cleanups = []), t._x_cleanups.push(e)) + : ((e = t), nr.push(e)); + } + function Tt(t) { + rr.push(t); + } + function Rt(t, e, r) { + (t._x_attributeCleanups || (t._x_attributeCleanups = {}), + t._x_attributeCleanups[e] || (t._x_attributeCleanups[e] = []), + t._x_attributeCleanups[e].push(r)); + } + function fe(t, e) { + t._x_attributeCleanups && + Object.entries(t._x_attributeCleanups).forEach(([r, n]) => { + (e === void 0 || e.includes(r)) && + (n.forEach((i) => i()), delete t._x_attributeCleanups[r]); + }); + } + function sr(t) { + for (t._x_effects?.forEach(Ye); t._x_cleanups?.length; ) + t._x_cleanups.pop()(); + } + var de = new MutationObserver(_e), + pe = !1; + function mt() { + (de.observe(document, { + subtree: !0, + childList: !0, + attributes: !0, + attributeOldValue: !0, + }), + (pe = !0)); + } + function me() { + (Hn(), de.disconnect(), (pe = !1)); + } + var pt = []; + function Hn() { + let t = de.takeRecords(); + pt.push(() => t.length > 0 && _e(t)); + let e = pt.length; + queueMicrotask(() => { + if (pt.length === e) for (; pt.length > 0; ) pt.shift()(); + }); + } + function h(t) { + if (!pe) return t(); + me(); + let e = t(); + return (mt(), e); + } + var he = !1, + Ct = []; + function ar() { + he = !0; + } + function cr() { + ((he = !1), _e(Ct), (Ct = [])); + } + function _e(t) { + if (he) { + Ct = Ct.concat(t); + return; + } + let e = [], + r = new Set(), + n = new Map(), + i = new Map(); + for (let o = 0; o < t.length; o++) + if ( + !t[o].target._x_ignoreMutationObserver && + (t[o].type === "childList" && + (t[o].removedNodes.forEach((s) => { + s.nodeType === 1 && s._x_marker && r.add(s); + }), + t[o].addedNodes.forEach((s) => { + if (s.nodeType === 1) { + if (r.has(s)) { + r.delete(s); + return; + } + s._x_marker || e.push(s); + } + })), + t[o].type === "attributes") + ) { + let s = t[o].target, + a = t[o].attributeName, + c = t[o].oldValue, + l = () => { + (n.has(s) || n.set(s, []), + n.get(s).push({ name: a, value: s.getAttribute(a) })); + }, + u = () => { + (i.has(s) || i.set(s, []), i.get(s).push(a)); + }; + s.hasAttribute(a) && c === null + ? l() + : s.hasAttribute(a) + ? (u(), l()) + : u(); + } + (i.forEach((o, s) => { + fe(s, o); + }), + n.forEach((o, s) => { + rr.forEach((a) => a(s, o)); + })); + for (let o of r) e.some((s) => s.contains(o)) || nr.forEach((s) => s(o)); + for (let o of e) o.isConnected && ir.forEach((s) => s(o)); + ((e = null), (r = null), (n = null), (i = null)); + } + function Mt(t) { + return I(H(t)); + } + function P(t, e, r) { + return ( + (t._x_dataStack = [e, ...H(r || t)]), + () => { + t._x_dataStack = t._x_dataStack.filter((n) => n !== e); + } + ); + } + function H(t) { + return t._x_dataStack + ? t._x_dataStack + : typeof ShadowRoot == "function" && t instanceof ShadowRoot + ? H(t.host) + : t.parentNode + ? H(t.parentNode) + : []; + } + function I(t) { + return new Proxy({ objects: t }, Kn); + } + var Kn = { + ownKeys({ objects: t }) { + return Array.from(new Set(t.flatMap((e) => Object.keys(e)))); + }, + has({ objects: t }, e) { + return e == Symbol.unscopables + ? !1 + : t.some( + (r) => + Object.prototype.hasOwnProperty.call(r, e) || Reflect.has(r, e), + ); + }, + get({ objects: t }, e, r) { + return e == "toJSON" + ? Vn + : Reflect.get(t.find((n) => Reflect.has(n, e)) || {}, e, r); + }, + set({ objects: t }, e, r, n) { + let i = + t.find((s) => Object.prototype.hasOwnProperty.call(s, e)) || + t[t.length - 1], + o = Object.getOwnPropertyDescriptor(i, e); + return o?.set && o?.get ? o.set.call(n, r) || !0 : Reflect.set(i, e, r); + }, + }; + function Vn() { + return Reflect.ownKeys(this).reduce( + (e, r) => ((e[r] = Reflect.get(this, r)), e), + {}, + ); + } + function ot(t) { + let e = (n) => typeof n == "object" && !Array.isArray(n) && n !== null, + r = (n, i = "") => { + Object.entries(Object.getOwnPropertyDescriptors(n)).forEach( + ([o, { value: s, enumerable: a }]) => { + if ( + a === !1 || + s === void 0 || + (typeof s == "object" && s !== null && s.__v_skip) + ) + return; + let c = i === "" ? o : `${i}.${o}`; + typeof s == "object" && s !== null && s._x_interceptor + ? (n[o] = s.initialize(t, c, o)) + : e(s) && s !== n && !(s instanceof Element) && r(s, c); + }, + ); + }; + return r(t); + } + function Nt(t, e = () => {}) { + let r = { + initialValue: void 0, + _x_interceptor: !0, + initialize(n, i, o) { + return t( + this.initialValue, + () => Un(n, i), + (s) => ge(n, i, s), + i, + o, + ); + }, + }; + return ( + e(r), + (n) => { + if (typeof n == "object" && n !== null && n._x_interceptor) { + let i = r.initialize.bind(r); + r.initialize = (o, s, a) => { + let c = n.initialize(o, s, a); + return ((r.initialValue = c), i(o, s, a)); + }; + } else r.initialValue = n; + return r; + } + ); + } + function Un(t, e) { + return e.split(".").reduce((r, n) => r[n], t); + } + function ge(t, e, r) { + if ((typeof e == "string" && (e = e.split(".")), e.length === 1)) + t[e[0]] = r; + else { + if (e.length === 0) throw error; + return (t[e[0]] || (t[e[0]] = {}), ge(t[e[0]], e.slice(1), r)); + } + } + var lr = {}; + function y(t, e) { + lr[t] = e; + } + function U(t, e) { + let r = qn(e); + return ( + Object.entries(lr).forEach(([n, i]) => { + Object.defineProperty(t, `$${n}`, { + get() { + return i(e, r); + }, + enumerable: !1, + }); + }), + t + ); + } + function qn(t) { + let [e, r] = xe(t), + n = { interceptor: Nt, ...e }; + return (it(t, r), n); + } + function ur(t, e, r, ...n) { + try { + return r(...n); + } catch (i) { + st(i, t, e); + } + } + function st(...t) { + return fr(...t); + } + var fr = Wn; + function dr(t) { + fr = t; + } + function Wn(t, e, r = void 0) { + ((t = Object.assign(t ?? { message: "No error message given." }, { + el: e, + expression: r, + })), + console.warn( + `Alpine Expression Error: ${t.message} + +${ + r + ? 'Expression: "' + + r + + `" + +` + : "" +}`, + e, + ), + setTimeout(() => { + throw t; + }, 0)); + } + var at = !0; + function kt(t) { + let e = at; + at = !1; + let r = t(); + return ((at = e), r); + } + function N(t, e, r = {}) { + let n; + return (x(t, e)((i) => (n = i), r), n); + } + function x(...t) { + return pr(...t); + } + var pr = be; + function mr(t) { + pr = t; + } + var hr; + function _r(t) { + hr = t; + } + function be(t, e) { + let r = {}; + U(r, t); + let n = [r, ...H(t)], + i = typeof e == "function" ? Gn(n, e) : Yn(n, e, t); + return ur.bind(null, t, e, i); + } + function Gn(t, e) { + return ( + r = () => {}, + { scope: n = {}, params: i = [], context: o } = {}, + ) => { + if (!at) { + ht(r, e, I([n, ...t]), i); + return; + } + let s = e.apply(I([n, ...t]), i); + ht(r, s); + }; + } + var ye = {}; + function Jn(t, e) { + if (ye[t]) return ye[t]; + let r = Object.getPrototypeOf(async function () {}).constructor, + n = + /^[\n\s]*if.*\(.*\)/.test(t.trim()) || /^(let|const)\s/.test(t.trim()) + ? `(async()=>{ ${t} })()` + : t, + o = (() => { + try { + let s = new r( + ["__self", "scope"], + `with (scope) { __self.result = ${n} }; __self.finished = true; return __self.result;`, + ); + return ( + Object.defineProperty(s, "name", { value: `[Alpine] ${t}` }), + s + ); + } catch (s) { + return (st(s, e, t), Promise.resolve()); + } + })(); + return ((ye[t] = o), o); + } + function Yn(t, e, r) { + let n = Jn(e, r); + return ( + i = () => {}, + { scope: o = {}, params: s = [], context: a } = {}, + ) => { + ((n.result = void 0), (n.finished = !1)); + let c = I([o, ...t]); + if (typeof n == "function") { + let l = n.call(a, n, c).catch((u) => st(u, r, e)); + n.finished + ? (ht(i, n.result, c, s, r), (n.result = void 0)) + : l + .then((u) => { + ht(i, u, c, s, r); + }) + .catch((u) => st(u, r, e)) + .finally(() => (n.result = void 0)); + } + }; + } + function ht(t, e, r, n, i) { + if (at && typeof e == "function") { + let o = e.apply(r, n); + o instanceof Promise + ? o.then((s) => ht(t, s, r, n)).catch((s) => st(s, i, e)) + : t(o); + } else + typeof e == "object" && e instanceof Promise ? e.then((o) => t(o)) : t(e); + } + function gr(...t) { + return hr(...t); + } + function xr(t, e, r = {}) { + let n = {}; + U(n, t); + let i = [n, ...H(t)], + o = I([r.scope ?? {}, ...i]), + s = r.params ?? []; + if (e.includes("await")) { + let a = Object.getPrototypeOf(async function () {}).constructor, + c = + /^[\n\s]*if.*\(.*\)/.test(e.trim()) || /^(let|const)\s/.test(e.trim()) + ? `(async()=>{ ${e} })()` + : e; + return new a( + ["scope"], + `with (scope) { let __result = ${c}; return __result }`, + ).call(r.context, o); + } else { + let a = + /^[\n\s]*if.*\(.*\)/.test(e.trim()) || /^(let|const)\s/.test(e.trim()) + ? `(()=>{ ${e} })()` + : e, + l = new Function( + ["scope"], + `with (scope) { let __result = ${a}; return __result }`, + ).call(r.context, o); + return typeof l == "function" && at ? l.apply(o, s) : l; + } + } + var ve = "x-"; + function T(t = "") { + return ve + t; + } + function yr(t) { + ve = t; + } + var Dt = {}; + function d(t, e) { + return ( + (Dt[t] = e), + { + before(r) { + if (!Dt[r]) { + console.warn( + String.raw`Cannot find directive \`${r}\`. \`${t}\` will use the default order of execution`, + ); + return; + } + let n = X.indexOf(r); + X.splice(n >= 0 ? n : X.indexOf("DEFAULT"), 0, t); + }, + } + ); + } + function br(t) { + return Object.keys(Dt).includes(t); + } + function gt(t, e, r) { + if (((e = Array.from(e)), t._x_virtualDirectives)) { + let o = Object.entries(t._x_virtualDirectives).map(([a, c]) => ({ + name: a, + value: c, + })), + s = Se(o); + ((o = o.map((a) => + s.find((c) => c.name === a.name) + ? { name: `x-bind:${a.name}`, value: `"${a.value}"` } + : a, + )), + (e = e.concat(o))); + } + let n = {}; + return e + .map(vr((o, s) => (n[o] = s))) + .filter(Ar) + .map(Zn(n, r)) + .sort(Qn) + .map((o) => Xn(t, o)); + } + function Se(t) { + return Array.from(t) + .map(vr()) + .filter((e) => !Ar(e)); + } + var we = !1, + _t = new Map(), + wr = Symbol(); + function Er(t) { + we = !0; + let e = Symbol(); + ((wr = e), _t.set(e, [])); + let r = () => { + for (; _t.get(e).length; ) _t.get(e).shift()(); + _t.delete(e); + }, + n = () => { + ((we = !1), r()); + }; + (t(r), n()); + } + function xe(t) { + let e = [], + r = (a) => e.push(a), + [n, i] = tr(t); + return ( + e.push(i), + [ + { + Alpine: K, + effect: n, + cleanup: r, + evaluateLater: x.bind(x, t), + evaluate: N.bind(N, t), + }, + () => e.forEach((a) => a()), + ] + ); + } + function Xn(t, e) { + let r = () => {}, + n = Dt[e.type] || r, + [i, o] = xe(t); + Rt(t, e.original, o); + let s = () => { + t._x_ignore || + t._x_ignoreSelf || + (n.inline && n.inline(t, e, i), + (n = n.bind(n, t, e, i)), + we ? _t.get(wr).push(n) : n()); + }; + return ((s.runCleanups = o), s); + } + var Pt = + (t, e) => + ({ name: r, value: n }) => ( + r.startsWith(t) && (r = r.replace(t, e)), + { name: r, value: n } + ), + It = (t) => t; + function vr(t = () => {}) { + return ({ name: e, value: r }) => { + let { name: n, value: i } = Sr.reduce((o, s) => s(o), { + name: e, + value: r, + }); + return (n !== e && t(n, e), { name: n, value: i }); + }; + } + var Sr = []; + function ct(t) { + Sr.push(t); + } + function Ar({ name: t }) { + return Or().test(t); + } + var Or = () => new RegExp(`^${ve}([^:^.]+)\\b`); + function Zn(t, e) { + return ({ name: r, value: n }) => { + r === n && (n = ""); + let i = r.match(Or()), + o = r.match(/:([a-zA-Z0-9\-_:]+)/), + s = r.match(/\.[^.\]]+(?=[^\]]*$)/g) || [], + a = e || t[r] || r; + return { + type: i ? i[1] : null, + value: o ? o[1] : null, + modifiers: s.map((c) => c.replace(".", "")), + expression: n, + original: a, + }; + }; + } + var Ee = "DEFAULT", + X = [ + "ignore", + "ref", + "data", + "id", + "anchor", + "bind", + "init", + "for", + "model", + "modelable", + "transition", + "show", + "if", + Ee, + "teleport", + ]; + function Qn(t, e) { + let r = X.indexOf(t.type) === -1 ? Ee : t.type, + n = X.indexOf(e.type) === -1 ? Ee : e.type; + return X.indexOf(r) - X.indexOf(n); + } + function Z(t, e, r = {}) { + t.dispatchEvent( + new CustomEvent(e, { + detail: r, + bubbles: !0, + composed: !0, + cancelable: !0, + }), + ); + } + function $(t, e) { + if (typeof ShadowRoot == "function" && t instanceof ShadowRoot) { + Array.from(t.children).forEach((i) => $(i, e)); + return; + } + let r = !1; + if ((e(t, () => (r = !0)), r)) return; + let n = t.firstElementChild; + for (; n; ) ($(n, e, !1), (n = n.nextElementSibling)); + } + function w(t, ...e) { + console.warn(`Alpine Warning: ${t}`, ...e); + } + var Cr = !1; + function Tr() { + (Cr && + w( + "Alpine has already been initialized on this page. Calling Alpine.start() more than once can cause problems.", + ), + (Cr = !0), + document.body || + w( + "Unable to initialize. Trying to load Alpine before `
` is available. Did you forget to add `defer` in Alpine's ` - + {{ block "extraheader" . }}{{ end }} - {{ if not .Config.IsProductionEnvironment }} + {{ if not .Config.IsProductionEnvironment }} {{ end }} diff --git a/html/template/sync/planning-root.html b/html/template/sync/planning-root.html index 8f71260e..632c5bc1 100644 --- a/html/template/sync/planning-root.html +++ b/html/template/sync/planning-root.html @@ -6,10 +6,6 @@ type="text/javascript" src="//unpkg.com/maplibre-gl@5.0.1/dist/maplibre-gl.js" > - diff --git a/html/template/sync/review/pool.html b/html/template/sync/review/pool.html index 2f4fbe71..03791472 100644 --- a/html/template/sync/review/pool.html +++ b/html/template/sync/review/pool.html @@ -6,10 +6,6 @@ type="text/javascript" src="//unpkg.com/maplibre-gl@5.0.1/dist/maplibre-gl.js" > - diff --git a/platform/notification.go b/platform/notification.go index b95fc53b..e1106134 100644 --- a/platform/notification.go +++ b/platform/notification.go @@ -27,6 +27,10 @@ type Notification struct { Time time.Time Type string } +type UserNotificationCounts struct { + Communications uint `json:"communication"` + Home uint `json:"home"` +} // Clear all notifications for a given user with the given path func ClearOauth(ctx context.Context, user *models.User) { @@ -100,6 +104,26 @@ func NotificationsForUser(ctx context.Context, u User) ([]Notification, error) { } return results, nil } +func NotificationCountsForUser(ctx context.Context, u User) (*UserNotificationCounts, error) { + count_home, err := u.model.UserNotifications( + models.SelectWhere.Notifications.ResolvedAt.IsNull(), + ).Count(ctx, db.PGInstance.BobDB) + if err != nil { + return nil, fmt.Errorf("Failed to get home notification count: %w", err) + } + count_nuisance, err := u.Organization.model.Waters().Count(ctx, db.PGInstance.BobDB) + if err != nil { + return nil, fmt.Errorf("Failed to get nuisance notification count: %w", err) + } + count_water, err := u.Organization.model.Waters().Count(ctx, db.PGInstance.BobDB) + if err != nil { + return nil, fmt.Errorf("Failed to get water notification count: %w", err) + } + return &UserNotificationCounts{ + Communications: uint(count_nuisance + count_water), + Home: uint(count_home), + }, nil +} func notificationTypeName(t enums.Notificationtype) string { switch t { diff --git a/platform/user.go b/platform/user.go index e79a353a..86889473 100644 --- a/platform/user.go +++ b/platform/user.go @@ -2,6 +2,7 @@ package platform import ( "context" + "encoding/json" "fmt" "strings" @@ -21,36 +22,51 @@ type NoUserError struct{} func (e NoUserError) Error() string { return "That user does not exist" } type User struct { - DisplayName string `json:"display_name"` - ID int `json:"-"` - Initials string `json:"initials"` - Notifications []Notification `json:"-"` - Organization Organization `json:"organization"` - PasswordHash string `json:"-"` - PasswordHashType string `json:"-"` - Role string `json:"role"` - Username string `json:"username"` + DisplayName string `json:"display_name"` + ID int `json:"-"` + Initials string `json:"initials"` + Notifications []Notification `json:"notifications"` + NotificationCounts UserNotificationCounts `json:"notification_counts"` + Organization Organization `json:"organization"` + PasswordHash string `json:"-"` + PasswordHashType string `json:"-"` + Role string `json:"role"` + Username string `json:"username"` model *models.User } +func (u User) AsJSON() string { + content, err := json.Marshal(u) + if err != nil { + return fmt.Sprintf("{error: \"%s\"}", err.Error()) + } + return string(content) +} func (u User) HasRoot() bool { return u.model.Role == enums.UserroleRoot } -func newUser(org Organization, user *models.User) User { - return User{ - DisplayName: user.DisplayName, - ID: int(user.ID), - Initials: extractInitials(user.DisplayName), - Notifications: []Notification{}, - Organization: org, - PasswordHash: user.PasswordHash, - PasswordHashType: string(user.PasswordHashType), - Role: user.Role.String(), - Username: user.Username, +func newUser(ctx context.Context, org Organization, user *models.User) User { + u := User{ + DisplayName: user.DisplayName, + ID: int(user.ID), + Initials: extractInitials(user.DisplayName), + Notifications: []Notification{}, + NotificationCounts: UserNotificationCounts{}, + Organization: org, + PasswordHash: user.PasswordHash, + PasswordHashType: string(user.PasswordHashType), + Role: user.Role.String(), + Username: user.Username, model: user, } + counts, err := NotificationCountsForUser(ctx, u) + if err != nil { + log.Error().Err(err).Int32("id", user.ID).Msg("failed to get notification counts for user") + } + u.NotificationCounts = *counts + return u } func CreateUser(ctx context.Context, username string, name string, password_hash string) (*User, error) { @@ -75,7 +91,7 @@ func CreateUser(ctx context.Context, username string, name string, password_hash return nil, fmt.Errorf("Failed to create user: %w", err) } log.Info().Int32("id", user.ID).Str("username", user.Username).Msg("Created user") - u := newUser(newOrganization(o), user) + u := newUser(ctx, newOrganization(o), user) return &u, nil } func UserByID(ctx context.Context, user_id int32) (*User, error) { @@ -91,7 +107,7 @@ func UsersByOrg(ctx context.Context, org Organization) (map[int32]*User, error) } results := make(map[int32]*User, len(users)) for _, user := range users { - u := newUser(org, user) + u := newUser(ctx, org, user) results[user.ID] = &u } return results, nil @@ -113,7 +129,7 @@ func getUser(ctx context.Context, where mods.Where[*dialect.SelectQuery]) (*User } org := newOrganization(user.R.Organization) - u := newUser(org, user) + u := newUser(ctx, org, user) return &u, nil } func extractInitials(name string) string {