This commit is contained in:
parent
8b203908a0
commit
725945d95c
22 changed files with 381 additions and 135 deletions
|
|
@ -30,7 +30,7 @@ type handlerFunctionDelete func(context.Context, *http.Request, platform.User) *
|
|||
type handlerFunctionGet[T any] func(context.Context, *http.Request, resource.QueryParams) (*T, *nhttp.ErrorWithStatus)
|
||||
type handlerFunctionGetAuthenticated[T any] func(context.Context, *http.Request, platform.User, resource.QueryParams) (T, *nhttp.ErrorWithStatus)
|
||||
type handlerFunctionGetImage func(context.Context, *http.Request, platform.User) (file.Collection, uuid.UUID, *nhttp.ErrorWithStatus)
|
||||
type handlerFunctionGetSlice[T any] func(context.Context, *http.Request, resource.QueryParams) ([]*T, *nhttp.ErrorWithStatus)
|
||||
type handlerFunctionGetSlice[T any] func(context.Context, *http.Request, resource.QueryParams) ([]T, *nhttp.ErrorWithStatus)
|
||||
type handlerFunctionGetSliceAuthenticated[T any] func(context.Context, *http.Request, platform.User, resource.QueryParams) ([]T, *nhttp.ErrorWithStatus)
|
||||
type handlerFunctionPost[RequestType any, ResponseType any] func(context.Context, *http.Request, RequestType) (ResponseType, *nhttp.ErrorWithStatus)
|
||||
type handlerFunctionPostAuthenticated[RequestType any, ResponseType any] func(context.Context, *http.Request, platform.User, RequestType) (ResponseType, *nhttp.ErrorWithStatus)
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ func AddRoutesRMO(r *mux.Router) {
|
|||
func AddRoutesSync(r *mux.Router) {
|
||||
router := resource.NewRouter(r)
|
||||
|
||||
contact := resource.Contact(router)
|
||||
compliance_request := resource.ComplianceRequest(router)
|
||||
district := resource.District(router)
|
||||
geocode := resource.Geocode(router)
|
||||
|
|
@ -107,6 +108,8 @@ func AddRoutesSync(r *mux.Router) {
|
|||
r.Handle("/compliance-request/mailer", authenticatedHandlerJSONPost(compliance_request.CreateMailer)).Methods("POST")
|
||||
//r.HandleFunc("/compliance-request/image/pool/{public_id}", getComplianceRequestImagePool).Methods("GET")
|
||||
r.Handle("/configuration/integration/arcgis", authenticatedHandlerJSONPost(postConfigurationIntegrationArcgis)).Methods("POST")
|
||||
r.Handle("/contact", authenticatedHandlerJSONSlice(contact.List)).Methods("GET")
|
||||
r.Handle("/contact/{id}", authenticatedHandlerJSON(contact.ByIDGet)).Methods("GET").Name("contact.ByIDGet")
|
||||
email := resource.Email(router)
|
||||
r.Handle("/email/{id}", authenticatedHandlerJSON(email.Get)).Methods("GET").Name("email.ByIDGet")
|
||||
r.Handle("/events", auth.NewEnsureAuth(streamEvents)).Methods("GET")
|
||||
|
|
|
|||
|
|
@ -35,13 +35,10 @@ func ContactUpdateName(ctx context.Context, txn db.Ex, id int64, name string) er
|
|||
return db.ExecuteNoneTx(ctx, txn, statement)
|
||||
}
|
||||
|
||||
/*
|
||||
func ContactsFromAddress(ctx context.Context, address string) ([]model.Contact, error) {
|
||||
func ContactsFromOrganizationID(ctx context.Context, txn db.Ex, org_id int64) ([]model.Contact, error) {
|
||||
statement := table.Contact.SELECT(
|
||||
table.Contact.AllColumns,
|
||||
).FROM(table.Contact).
|
||||
WHERE(table.Contact.Source.EQ(postgres.String(address)).OR(
|
||||
table.Contact.Destination.EQ(postgres.String(address))))
|
||||
return db.ExecuteMany[model.Contact](ctx, statement)
|
||||
WHERE(table.Contact.OrganizationID.EQ(postgres.Int(org_id)))
|
||||
return db.ExecuteManyTx[model.Contact](ctx, txn, statement)
|
||||
}
|
||||
*/
|
||||
|
|
|
|||
52
db/query/comms/contact_email.go
Normal file
52
db/query/comms/contact_email.go
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
package comms
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
//"github.com/Gleipnir-Technology/bob"
|
||||
"github.com/Gleipnir-Technology/nidus-sync/db"
|
||||
//"github.com/Gleipnir-Technology/nidus-sync/db/gen/nidus-sync/public/enum"
|
||||
"github.com/Gleipnir-Technology/jet/postgres"
|
||||
"github.com/Gleipnir-Technology/nidus-sync/db/gen/nidus-sync/comms/model"
|
||||
"github.com/Gleipnir-Technology/nidus-sync/db/gen/nidus-sync/comms/table"
|
||||
)
|
||||
|
||||
func ContactEmailInsert(ctx context.Context, txn db.Ex, m model.ContactEmail) (model.ContactEmail, error) {
|
||||
statement := table.ContactEmail.INSERT(table.ContactEmail.MutableColumns).
|
||||
MODEL(m).
|
||||
RETURNING(table.ContactEmail.AllColumns)
|
||||
return db.ExecuteOneTx[model.ContactEmail](ctx, txn, statement)
|
||||
}
|
||||
|
||||
func ContactEmailFromAddress(ctx context.Context, txn db.Ex, address string) (model.ContactEmail, error) {
|
||||
statement := table.ContactEmail.SELECT(
|
||||
table.ContactEmail.AllColumns,
|
||||
).FROM(table.ContactEmail).
|
||||
WHERE(table.ContactEmail.Address.EQ(postgres.String(address)))
|
||||
return db.ExecuteOneTx[model.ContactEmail](ctx, txn, statement)
|
||||
}
|
||||
func ContactEmailByContactIDs(ctx context.Context, txn db.Ex, contact_ids []int64) (result map[int64][]model.ContactEmail, err error) {
|
||||
sql_ids := make([]postgres.Expression, len(contact_ids))
|
||||
for i, contact_id := range contact_ids {
|
||||
sql_ids[i] = postgres.Int(contact_id)
|
||||
}
|
||||
statement := table.ContactEmail.SELECT(
|
||||
table.ContactEmail.AllColumns,
|
||||
).FROM(table.ContactEmail).
|
||||
WHERE(table.ContactEmail.ContactID.IN(sql_ids...))
|
||||
rows, err := db.ExecuteManyTx[model.ContactEmail](ctx, txn, statement)
|
||||
if err != nil {
|
||||
return result, fmt.Errorf("query by contact IDs: %w", err)
|
||||
}
|
||||
for _, contact_id := range contact_ids {
|
||||
result[contact_id] = make([]model.ContactEmail, 0)
|
||||
}
|
||||
for _, row := range rows {
|
||||
id := int64(row.ContactID)
|
||||
cur := result[id]
|
||||
cur = append(cur, row)
|
||||
result[id] = cur
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ package comms
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
//"github.com/Gleipnir-Technology/bob"
|
||||
"github.com/Gleipnir-Technology/nidus-sync/db"
|
||||
|
|
@ -46,14 +47,27 @@ func ContactPhoneUpdateStopMessageID(ctx context.Context, txn db.Ex, e164 string
|
|||
WHERE(table.ContactPhone.E164.EQ(postgres.String(e164)))
|
||||
return db.ExecuteNoneTx(ctx, txn, statement)
|
||||
}
|
||||
|
||||
/*
|
||||
func ContactPhonesFromAddress(ctx context.Context, address string) ([]model.ContactPhone, error) {
|
||||
func ContactPhoneByContactIDs(ctx context.Context, txn db.Ex, contact_ids []int64) (result map[int64][]model.ContactPhone, err error) {
|
||||
sql_ids := make([]postgres.Expression, len(contact_ids))
|
||||
for i, contact_id := range contact_ids {
|
||||
sql_ids[i] = postgres.Int(contact_id)
|
||||
}
|
||||
statement := table.ContactPhone.SELECT(
|
||||
table.ContactPhone.AllColumns,
|
||||
).FROM(table.ContactPhone).
|
||||
WHERE(table.ContactPhone.Source.EQ(postgres.String(address)).OR(
|
||||
table.ContactPhone.Destination.EQ(postgres.String(address))))
|
||||
return db.ExecuteMany[model.ContactPhone](ctx, statement)
|
||||
WHERE(table.ContactPhone.ContactID.IN(sql_ids...))
|
||||
rows, err := db.ExecuteManyTx[model.ContactPhone](ctx, txn, statement)
|
||||
if err != nil {
|
||||
return result, fmt.Errorf("query by contact IDs: %w", err)
|
||||
}
|
||||
for _, contact_id := range contact_ids {
|
||||
result[contact_id] = make([]model.ContactPhone, 0)
|
||||
}
|
||||
for _, row := range rows {
|
||||
id := int64(row.ContactID)
|
||||
cur := result[id]
|
||||
cur = append(cur, row)
|
||||
result[id] = cur
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
*/
|
||||
|
|
|
|||
70
db/query/public/mailer.go
Normal file
70
db/query/public/mailer.go
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
package public
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/Gleipnir-Technology/jet/postgres"
|
||||
"github.com/Gleipnir-Technology/nidus-sync/db"
|
||||
"github.com/Gleipnir-Technology/nidus-sync/db/gen/nidus-sync/public/table"
|
||||
"github.com/Gleipnir-Technology/nidus-sync/platform/types"
|
||||
)
|
||||
|
||||
func mailerBaseQuery() postgres.SelectStatement {
|
||||
return table.ComplianceReportRequest.SELECT(
|
||||
table.Address.AllColumns,
|
||||
table.ComplianceReportRequest.AllColumns,
|
||||
).FROM(
|
||||
table.ComplianceReportRequest,
|
||||
).FROM(
|
||||
table.ComplianceReportRequest.INNER_JOIN(
|
||||
table.ComplianceReportRequestMailer,
|
||||
table.ComplianceReportRequestMailer.ComplianceReportRequestID.EQ(
|
||||
table.ComplianceReportRequest.ID),
|
||||
),
|
||||
).FROM(
|
||||
table.ComplianceReportRequest.INNER_JOIN(
|
||||
table.Lead,
|
||||
table.Lead.ID.EQ(
|
||||
table.ComplianceReportRequest.LeadID,
|
||||
),
|
||||
),
|
||||
).FROM(
|
||||
table.Lead.INNER_JOIN(
|
||||
table.Site,
|
||||
table.Site.ID.EQ(
|
||||
table.Lead.SiteID,
|
||||
),
|
||||
),
|
||||
).FROM(
|
||||
table.Site.INNER_JOIN(
|
||||
table.Address,
|
||||
table.Address.ID.EQ(
|
||||
table.Site.AddressID,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
func MailerFromPublicID(ctx context.Context, txn db.Ex, org_id int64, id int64) (*types.Mailer, error) {
|
||||
statement := mailerBaseQuery().WHERE(
|
||||
table.ComplianceReportRequest.ID.EQ(postgres.Int(id)).AND(
|
||||
table.Site.OrganizationID.EQ(postgres.Int(org_id))),
|
||||
)
|
||||
row, err := db.ExecuteOneTx[types.Mailer](ctx, txn, statement)
|
||||
if err != nil {
|
||||
if errors.Is(err, db.ErrNoRows) {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, fmt.Errorf("query: %w", err)
|
||||
}
|
||||
return &row, nil
|
||||
}
|
||||
func MailersFromOrganizationID(ctx context.Context, txn db.Ex, org_id int64, limit int64) ([]types.Mailer, error) {
|
||||
statement := mailerBaseQuery().WHERE(
|
||||
table.Site.OrganizationID.EQ(postgres.Int(org_id)),
|
||||
).ORDER_BY(
|
||||
table.ComplianceReportRequest.Created,
|
||||
).LIMIT(limit)
|
||||
return db.ExecuteManyTx[types.Mailer](ctx, txn, statement)
|
||||
}
|
||||
54
platform/contact.go
Normal file
54
platform/contact.go
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
package platform
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/Gleipnir-Technology/nidus-sync/db"
|
||||
querycomms "github.com/Gleipnir-Technology/nidus-sync/db/query/comms"
|
||||
"github.com/Gleipnir-Technology/nidus-sync/platform/types"
|
||||
)
|
||||
|
||||
func ContactsForOrganization(ctx context.Context, org_id int32) (results []types.Contact, err error) {
|
||||
txn := db.PGInstance.PGXPool
|
||||
rows, err := querycomms.ContactsFromOrganizationID(ctx, txn, int64(org_id))
|
||||
if err != nil {
|
||||
return results, fmt.Errorf("contacts from organization id: %w", err)
|
||||
}
|
||||
contact_ids := make([]int64, len(rows))
|
||||
for i, row := range rows {
|
||||
contact_ids[i] = int64(row.ID)
|
||||
}
|
||||
contact_emails_by_contact_id, err := querycomms.ContactEmailByContactIDs(ctx, txn, contact_ids)
|
||||
if err != nil {
|
||||
return results, fmt.Errorf("by contact ids: %w", err)
|
||||
}
|
||||
contact_phones_by_contact_id, err := querycomms.ContactPhoneByContactIDs(ctx, txn, contact_ids)
|
||||
if err != nil {
|
||||
return results, fmt.Errorf("by contact ids: %w", err)
|
||||
}
|
||||
|
||||
results = make([]types.Contact, len(rows))
|
||||
for i, row := range rows {
|
||||
contact_emails := contact_emails_by_contact_id[int64(row.ID)]
|
||||
emails := make([]string, len(contact_emails))
|
||||
for i, e := range contact_emails {
|
||||
emails[i] = e.Address
|
||||
}
|
||||
contact_phones := contact_phones_by_contact_id[int64(row.ID)]
|
||||
phones := make([]types.Phone, len(contact_phones))
|
||||
for i, p := range contact_phones {
|
||||
phones[i] = types.Phone{
|
||||
E164: p.E164,
|
||||
CanSMS: p.CanSms,
|
||||
}
|
||||
}
|
||||
results[i] = types.Contact{
|
||||
Emails: emails,
|
||||
ID: row.ID,
|
||||
Name: row.Name,
|
||||
Phones: phones,
|
||||
}
|
||||
}
|
||||
return results, nil
|
||||
}
|
||||
|
|
@ -2,7 +2,6 @@ package platform
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/Gleipnir-Technology/bob"
|
||||
"github.com/Gleipnir-Technology/bob/dialect/psql"
|
||||
|
|
@ -10,34 +9,12 @@ import (
|
|||
"github.com/Gleipnir-Technology/bob/dialect/psql/sm"
|
||||
"github.com/Gleipnir-Technology/nidus-sync/db"
|
||||
"github.com/Gleipnir-Technology/nidus-sync/db/models"
|
||||
querypublic "github.com/Gleipnir-Technology/nidus-sync/db/query/public"
|
||||
"github.com/Gleipnir-Technology/nidus-sync/platform/types"
|
||||
"github.com/stephenafamo/scan"
|
||||
)
|
||||
|
||||
func MailerByID(ctx context.Context, user User, id int32) (*types.Mailer, error) {
|
||||
query := mailerQuery()
|
||||
query.Apply(
|
||||
sm.Where(models.ComplianceReportRequests.Columns.ID.EQ(psql.Arg(id))),
|
||||
sm.Where(
|
||||
models.Sites.Columns.OrganizationID.EQ(psql.Arg(user.Organization.ID)),
|
||||
),
|
||||
)
|
||||
mailers, err := mailerQueryToRows(ctx, query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return mailers[id], nil
|
||||
}
|
||||
func MailerList(ctx context.Context, user User, limit int) ([]*types.Mailer, error) {
|
||||
query := mailerQuery()
|
||||
query.Apply(
|
||||
sm.Where(
|
||||
models.Sites.Columns.OrganizationID.EQ(psql.Arg(user.Organization.ID)),
|
||||
),
|
||||
sm.OrderBy(models.ComplianceReportRequests.Columns.Created),
|
||||
sm.Limit(limit),
|
||||
)
|
||||
return mailerQueryToRows(ctx, query)
|
||||
func MailerList(ctx context.Context, user User, limit int) ([]types.Mailer, error) {
|
||||
return querypublic.MailersFromOrganizationID(ctx, db.PGInstance.PGXPool, int64(user.Organization.ID), int64(limit))
|
||||
}
|
||||
func mailerQuery() bob.BaseQuery[*dialect.SelectQuery] {
|
||||
return psql.Select(
|
||||
|
|
@ -78,11 +55,3 @@ func mailerQuery() bob.BaseQuery[*dialect.SelectQuery] {
|
|||
),
|
||||
)
|
||||
}
|
||||
func mailerQueryToRows(ctx context.Context, query bob.BaseQuery[*dialect.SelectQuery]) ([]*types.Mailer, error) {
|
||||
rows, err := bob.All(ctx, db.PGInstance.BobDB, query, scan.StructMapper[*types.Mailer]())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("query mailers: %w", err)
|
||||
}
|
||||
|
||||
return rows, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -143,13 +143,13 @@ func reportQueryToRows(ctx context.Context, reports []modelpublicreport.Report,
|
|||
DistrictID: &row.OrganizationID,
|
||||
District: nil,
|
||||
PublicID: row.PublicID,
|
||||
Reporter: types.Contact{
|
||||
CanSMS: &row.ReporterPhoneCanSms,
|
||||
Email: &row.ReporterEmail,
|
||||
HasEmail: row.ReporterEmail != "",
|
||||
HasPhone: row.ReporterPhone != "",
|
||||
Name: row.ReporterName,
|
||||
Phone: &row.ReporterPhone,
|
||||
Reporter: types.ContactReporter{
|
||||
Email: row.ReporterEmail,
|
||||
Name: row.ReporterName,
|
||||
Phone: types.PhoneReporter{
|
||||
CanSMS: row.ReporterPhoneCanSms,
|
||||
Number: row.ReporterPhone,
|
||||
},
|
||||
},
|
||||
Status: row.Status.String(),
|
||||
Type: row.ReportType.String(),
|
||||
|
|
|
|||
|
|
@ -1,37 +1,32 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
//"github.com/rs/zerolog/log"
|
||||
//"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
type Contact struct {
|
||||
CanSMS *bool `db:"can_sms" json:"can_sms"`
|
||||
Email *string `db:"email" json:"email"`
|
||||
HasEmail bool `json:"has_email"`
|
||||
HasPhone bool `json:"has_phone"`
|
||||
Name string `db:"name" json:"name"`
|
||||
Phone *string `db:"phone" json:"phone"`
|
||||
Emails []string `json:"emails"`
|
||||
ID int32 `json:"-"`
|
||||
Name string `json:"name"`
|
||||
Phones []Phone `json:"phones"`
|
||||
}
|
||||
type ContactReporter struct {
|
||||
Email string `json:"email"`
|
||||
Name string `json:"name"`
|
||||
Phone PhoneReporter `json:"phone"`
|
||||
}
|
||||
type Phone struct {
|
||||
E164 string `json:"e164"`
|
||||
CanSMS bool `json:"can_sms"`
|
||||
}
|
||||
type PhoneReporter struct {
|
||||
CanSMS bool `json:"can_sms"`
|
||||
Number string `json:"number"`
|
||||
}
|
||||
|
||||
func (c Contact) MarshalJSON() ([]byte, error) {
|
||||
to_marshal := make(map[string]interface{}, 0)
|
||||
if c.CanSMS != nil {
|
||||
to_marshal["can_sms"] = *c.CanSMS
|
||||
}
|
||||
to_marshal["name"] = c.Name
|
||||
to_marshal["has_email"] = (c.Email != nil && *c.Email != "")
|
||||
to_marshal["has_phone"] = (c.Phone != nil && *c.Phone != "")
|
||||
if c.Email != nil {
|
||||
to_marshal["email"] = *c.Email
|
||||
} else {
|
||||
to_marshal["email"] = ""
|
||||
}
|
||||
if c.Phone != nil {
|
||||
to_marshal["phone"] = *c.Phone
|
||||
} else {
|
||||
to_marshal["phone"] = ""
|
||||
}
|
||||
//log.Debug().Msg("marshaling contact")
|
||||
return json.Marshal(to_marshal)
|
||||
/*
|
||||
func ContactFromModel(m model.Contact) Contact {
|
||||
return Contact{
|
||||
Emails:
|
||||
}
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ type PublicReport struct {
|
|||
DistrictID *int32 `db:"organization_id" json:"-"`
|
||||
District *string `db:"-" json:"district"`
|
||||
PublicID string `db:"public_id" json:"public_id"`
|
||||
Reporter Contact `db:"reporter" json:"reporter"`
|
||||
Reporter ContactReporter `db:"reporter" json:"reporter"`
|
||||
Status string `db:"status" json:"status"`
|
||||
Type string `db:"report_type" json:"type"`
|
||||
URI string `db:"-" json:"uri"`
|
||||
|
|
|
|||
|
|
@ -40,8 +40,11 @@ func SiteFromModel(s *models.Site) Site {
|
|||
Notes: s.Notes,
|
||||
OrganizationID: s.OrganizationID,
|
||||
Owner: Contact{
|
||||
Name: s.OwnerName,
|
||||
Phone: &owner_phone,
|
||||
Name: s.OwnerName,
|
||||
Phones: []Phone{Phone{
|
||||
E164: owner_phone,
|
||||
CanSMS: false,
|
||||
}},
|
||||
},
|
||||
ResidentOwned: resident_owned,
|
||||
//ParcelID: s.ParcelID,
|
||||
|
|
|
|||
48
resource/contact.go
Normal file
48
resource/contact.go
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
package resource
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
nhttp "github.com/Gleipnir-Technology/nidus-sync/http"
|
||||
"github.com/Gleipnir-Technology/nidus-sync/platform"
|
||||
"github.com/Gleipnir-Technology/nidus-sync/platform/types"
|
||||
//"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
type contactR struct {
|
||||
router *router
|
||||
}
|
||||
|
||||
func Contact(r *router) *contactR {
|
||||
return &contactR{
|
||||
router: r,
|
||||
}
|
||||
}
|
||||
|
||||
type contact struct {
|
||||
types.Contact
|
||||
URI string
|
||||
}
|
||||
|
||||
func (res *contactR) ByIDGet(ctx context.Context, r *http.Request, user platform.User, qp QueryParams) (contact, *nhttp.ErrorWithStatus) {
|
||||
return contact{}, nil
|
||||
}
|
||||
func (res *contactR) List(ctx context.Context, r *http.Request, user platform.User, query QueryParams) ([]contact, *nhttp.ErrorWithStatus) {
|
||||
contacts, err := platform.ContactsForOrganization(ctx, user.Organization.ID)
|
||||
if err != nil {
|
||||
return nil, nhttp.NewError("nuisance report query: %w", err)
|
||||
}
|
||||
result := make([]contact, len(contacts))
|
||||
for i, c := range contacts {
|
||||
uri, err := res.router.IDToURI("contact.ByIDGet", int(c.ID))
|
||||
if err != nil {
|
||||
return nil, nhttp.NewError("contact uri: %w", err)
|
||||
}
|
||||
result[i] = contact{
|
||||
Contact: c,
|
||||
URI: uri,
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
|
@ -61,21 +61,21 @@
|
|||
</div>
|
||||
<div class="col-md-6">
|
||||
<label
|
||||
v-if="report.reporter.has_email"
|
||||
v-if="report.reporter.emails.length"
|
||||
class="form-label text-muted small mb-0"
|
||||
>
|
||||
<i class="bi bi-envelope"></i>
|
||||
<a :href="'mailto:' + report.reporter.email">{{
|
||||
report.reporter.email
|
||||
<a :href="'mailto:' + report.reporter.emails[0]">{{
|
||||
report.reporter.emails[0]
|
||||
}}</a>
|
||||
</label>
|
||||
<label
|
||||
v-if="report.reporter.has_phone"
|
||||
v-if="report.reporter.phones.length"
|
||||
class="form-label text-muted small mb-0"
|
||||
>
|
||||
<i class="bi bi-phone"></i>
|
||||
<a :href="'tel:+' + report.reporter.phone">{{
|
||||
report.reporter.phone
|
||||
<a :href="'tel:+' + report.reporter.phones[0]">{{
|
||||
report.reporter.phones[0]
|
||||
}}</a>
|
||||
</label>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -78,8 +78,8 @@
|
|||
<div
|
||||
v-if="
|
||||
!(
|
||||
selectedReport?.reporter.has_email ||
|
||||
selectedReport?.reporter.has_phone
|
||||
selectedReport?.reporter.emails.length ||
|
||||
selectedReport?.reporter.phones.length
|
||||
)
|
||||
"
|
||||
class="mb-3"
|
||||
|
|
@ -91,8 +91,8 @@
|
|||
</div>
|
||||
<div
|
||||
v-if="
|
||||
selectedReport?.reporter.has_email ||
|
||||
selectedReport?.reporter.has_phone
|
||||
selectedReport?.reporter.emails.length ||
|
||||
selectedReport?.reporter.phones.length
|
||||
"
|
||||
class="mb-3"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -87,13 +87,13 @@
|
|||
</div>
|
||||
<div class="col-md-6">
|
||||
<label
|
||||
v-if="report.owner.has_email"
|
||||
v-if="report.owner.emails.length"
|
||||
class="form-label text-muted small mb-0"
|
||||
>
|
||||
<i class="bi bi-envelope"></i>
|
||||
</label>
|
||||
<label
|
||||
v-if="report.owner.has_phone"
|
||||
v-if="report.owner.phones.length"
|
||||
class="form-label text-muted small mb-0"
|
||||
>
|
||||
<i class="bi bi-phone"></i>
|
||||
|
|
|
|||
|
|
@ -248,10 +248,8 @@ const hasCompleteResponse = computed(() => {
|
|||
r.images.length > 0 ||
|
||||
r.permission_type == PermissionType.GRANTED ||
|
||||
r.reporter.name ||
|
||||
r.reporter.phone ||
|
||||
r.reporter.has_phone ||
|
||||
r.reporter.email ||
|
||||
r.reporter.has_email
|
||||
r.reporter.phones.length ||
|
||||
r.reporter.emails.length
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@
|
|||
id="contact-phone"
|
||||
name="phone"
|
||||
placeholder="(555) 123-4567"
|
||||
v-model="modelValue.reporter.phone"
|
||||
v-model="modelValue.reporter.phone.number"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
|
@ -92,7 +92,7 @@
|
|||
<div
|
||||
class="alert alert-primary"
|
||||
role="alert"
|
||||
v-if="modelValue.reporter.has_email"
|
||||
v-if="modelValue.reporter.email != ''"
|
||||
>
|
||||
You've already added an email address to this report. If you alter the
|
||||
email below, it will replace the current email address.
|
||||
|
|
|
|||
|
|
@ -199,7 +199,7 @@
|
|||
<div
|
||||
class="summary-value"
|
||||
v-if="
|
||||
modelValue.reporter?.phone || modelValue.reporter?.has_phone
|
||||
modelValue.reporter?.phone || modelValue.reporter?.phone != ''
|
||||
"
|
||||
>
|
||||
{{ modelValue.reporter.phone }}
|
||||
|
|
@ -216,7 +216,7 @@
|
|||
<div
|
||||
class="summary-value"
|
||||
v-if="
|
||||
modelValue.reporter?.email || modelValue.reporter?.has_email
|
||||
modelValue.reporter?.email || modelValue.reporter?.email != ''
|
||||
"
|
||||
>
|
||||
{{ modelValue.reporter?.email }}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { defineStore } from "pinia";
|
||||
import { shallowRef } from "vue";
|
||||
import { ref, shallowRef } from "vue";
|
||||
|
||||
import { SSEManager, SSEMessageResource } from "@/SSEManager";
|
||||
import { useSessionStore } from "@/store/session";
|
||||
|
|
@ -8,6 +8,8 @@ import { apiClient } from "@/client";
|
|||
import {
|
||||
Communication,
|
||||
type CommunicationDTO,
|
||||
Contact,
|
||||
type ContactDTO,
|
||||
PublicReport,
|
||||
type PublicReportDTO,
|
||||
} from "@/type/api";
|
||||
|
|
@ -22,6 +24,7 @@ function createResourceStore<dto, full extends uriHaver>(
|
|||
from_json: jsonConverter<dto, full>,
|
||||
) {
|
||||
const _resourceByURI = shallowRef<Map<string, full>>(new Map());
|
||||
const _resourceFetchAll = ref<Promise<full[]> | null>(null);
|
||||
const _resourceFetchByURI = shallowRef<Map<string, Promise<full> | null>>(
|
||||
new Map(),
|
||||
);
|
||||
|
|
@ -33,20 +36,25 @@ function createResourceStore<dto, full extends uriHaver>(
|
|||
}
|
||||
});
|
||||
|
||||
async function byAll(): Promise<full[]> {
|
||||
const cur = _resourceFetchAll.value;
|
||||
if (cur) {
|
||||
return cur;
|
||||
}
|
||||
return fetchAll();
|
||||
}
|
||||
async function byID(id: string): Promise<full> {
|
||||
const uri = uriFromID(id);
|
||||
const cur = _resourceFetchByURI.value.get(uri);
|
||||
if (cur) {
|
||||
return cur;
|
||||
}
|
||||
return fetchByID(id);
|
||||
return byURI(uri);
|
||||
}
|
||||
async function byURI(uri: string): Promise<full> {
|
||||
const cur = _resourceFetchByURI.value.get(uri);
|
||||
let cur = _resourceFetchByURI.value.get(uri);
|
||||
if (cur) {
|
||||
return cur;
|
||||
}
|
||||
return fetchByURI(uri);
|
||||
cur = fetchByURI(uri);
|
||||
_resourceFetchByURI.value.set(uri, cur);
|
||||
return cur;
|
||||
}
|
||||
async function fetchAll(): Promise<full[]> {
|
||||
const sessionStore = useSessionStore();
|
||||
|
|
@ -88,9 +96,12 @@ function createResourceStore<dto, full extends uriHaver>(
|
|||
return `${api_base}/${id}`;
|
||||
}
|
||||
return {
|
||||
byAll,
|
||||
byID,
|
||||
byURI,
|
||||
fetchAll,
|
||||
fetchByID,
|
||||
fetchByURI,
|
||||
loadingURI,
|
||||
};
|
||||
}
|
||||
|
|
@ -101,6 +112,11 @@ export const useStoreResource = defineStore("resource", () => {
|
|||
"/communication",
|
||||
Communication.fromJSON,
|
||||
),
|
||||
contact: createResourceStore<ContactDTO, Contact>(
|
||||
"sync:contact",
|
||||
"/contact",
|
||||
Contact.fromJSON,
|
||||
),
|
||||
publicreport: createResourceStore<PublicReportDTO, PublicReport>(
|
||||
"sync:publicreport",
|
||||
"/publicreport",
|
||||
|
|
|
|||
|
|
@ -77,28 +77,47 @@ export class Bounds {
|
|||
}
|
||||
}
|
||||
export interface ContactOptions {
|
||||
can_sms: boolean;
|
||||
email?: string;
|
||||
has_email: boolean;
|
||||
has_phone: boolean;
|
||||
emails?: string[];
|
||||
name?: string;
|
||||
phone?: string;
|
||||
phones?: Phone[];
|
||||
uri?: string;
|
||||
}
|
||||
export interface Phone {
|
||||
can_sms: boolean;
|
||||
e164: string;
|
||||
}
|
||||
export interface PhoneReporter {
|
||||
can_sms: boolean;
|
||||
number: string;
|
||||
}
|
||||
export class Contact {
|
||||
can_sms: boolean;
|
||||
email: string;
|
||||
has_email: boolean;
|
||||
has_phone: boolean;
|
||||
emails: string[];
|
||||
name: string;
|
||||
phone: string;
|
||||
phones: Phone[];
|
||||
uri: string;
|
||||
constructor(options?: ContactOptions) {
|
||||
this.can_sms = options?.can_sms ?? false;
|
||||
this.email = options?.email ?? "";
|
||||
this.has_email = options?.has_email ?? false;
|
||||
this.has_phone = options?.has_phone ?? false;
|
||||
this.emails = options?.emails ?? [];
|
||||
this.name = options?.name ?? "";
|
||||
this.phone = options?.phone ?? "";
|
||||
this.phones = options?.phones ?? [];
|
||||
this.uri = options?.uri ?? "";
|
||||
}
|
||||
static fromJSON(json: ContactDTO): Contact {
|
||||
return new Contact(json);
|
||||
}
|
||||
}
|
||||
export interface ContactDTO {
|
||||
name: string;
|
||||
emails: string[];
|
||||
phones: Phone[];
|
||||
uri: string;
|
||||
}
|
||||
export class ContactReporter {
|
||||
constructor(
|
||||
public name: string,
|
||||
public email: string,
|
||||
public phone: Phone,
|
||||
public uri: string,
|
||||
) {}
|
||||
}
|
||||
export interface District {
|
||||
name: string;
|
||||
|
|
@ -195,7 +214,7 @@ export interface ComplianceUpdate {
|
|||
//images?: Image[];
|
||||
location?: Location;
|
||||
permission_type?: string;
|
||||
reporter?: Contact;
|
||||
reporter?: ContactReporter;
|
||||
submitted?: string;
|
||||
//uri: string;
|
||||
wants_scheduled?: boolean;
|
||||
|
|
@ -212,7 +231,7 @@ export interface PublicReportDTO {
|
|||
location: Location;
|
||||
log: LogEntryDTO[];
|
||||
public_id: string;
|
||||
reporter: Contact;
|
||||
reporter: ContactReporter;
|
||||
status: string;
|
||||
type: string;
|
||||
uri: string;
|
||||
|
|
@ -224,7 +243,7 @@ export interface PublicReportUpdate {
|
|||
images?: Image[];
|
||||
location?: Location;
|
||||
public_id?: string;
|
||||
reporter?: Contact;
|
||||
reporter?: ContactReporter;
|
||||
status?: string;
|
||||
type?: string;
|
||||
uri?: string;
|
||||
|
|
@ -242,7 +261,7 @@ export interface PublicReportOptions {
|
|||
location: Location;
|
||||
log: LogEntry[];
|
||||
public_id: string;
|
||||
reporter: Contact;
|
||||
reporter: ContactReporter;
|
||||
status: string;
|
||||
type: string;
|
||||
uri: string;
|
||||
|
|
@ -254,7 +273,7 @@ export class PublicReport {
|
|||
images: Image[];
|
||||
log: LogEntry[];
|
||||
public_id: string;
|
||||
reporter: Contact;
|
||||
reporter: ContactReporter;
|
||||
status: string;
|
||||
type: string;
|
||||
uri: string;
|
||||
|
|
@ -266,7 +285,7 @@ export class PublicReport {
|
|||
this.images = options?.images ?? [];
|
||||
this.log = options?.log ?? [];
|
||||
this.public_id = options?.public_id ?? "";
|
||||
this.reporter = options?.reporter ?? new Contact();
|
||||
this.reporter = options?.reporter ?? new ContactReporter();
|
||||
this.status = options?.status ?? "";
|
||||
this.type = options?.type ?? "";
|
||||
this.uri = options?.uri ?? "";
|
||||
|
|
|
|||
|
|
@ -12,8 +12,16 @@
|
|||
</ThreeColumn>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { computedAsync } from "@vueuse/core";
|
||||
|
||||
import ThreeColumn from "@/components/layout/ThreeColumn.vue";
|
||||
import ReviewContactColumnAction from "@/components/ReviewContactColumnAction.vue";
|
||||
import ReviewContactColumnDetail from "@/components/ReviewContactColumnDetail.vue";
|
||||
import ReviewContactColumnList from "@/components/ReviewContactColumnList.vue";
|
||||
import { useStoreResource } from "@/store/resource";
|
||||
|
||||
const storeResource = useStoreResource();
|
||||
const contacts = computedAsync(() => {
|
||||
return storeResource.contact.byAll();
|
||||
});
|
||||
</script>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue