From 32346e0aa96da48a1cbae5b5a685255c878d26e6 Mon Sep 17 00:00:00 2001 From: Martin Pander Date: Sat, 26 Nov 2022 18:41:39 +0100 Subject: [PATCH] Add Plan page with day planner --- api/dash_api.yaml | 224 ++++++++++++++++- api/generate_go_server.sh | 1 - backend/dash_backend.go | 9 +- backend/dashapi/api.go | 37 ++- .../{api_default.go => api_journal.go} | 32 +-- ...ault_service.go => api_journal_service.go} | 24 +- backend/dashapi/api_plan.go | 210 ++++++++++++++++ backend/dashapi/api_plan_service.go | 93 +++++++ backend/dashapi/model_plan_day.go | 53 ++++ backend/dashapi/model_plan_month.go | 43 ++++ backend/dashapi/model_plan_week.go | 48 ++++ backend/dashapi/model_plan_week_item.go | 45 ++++ backend/database/database.go | 11 - backend/database/models/plan.go | 25 ++ backend/database/pgDatabase.go | 60 ++++- backend/mapping/mapper.go | 15 +- backend/mapping/mapperImpl.go | 139 ++++++++++- backend/mapping/mapperImpl_test.go | 235 +++++++++++++++++- backend/service/api_journal_service.go | 67 +++++ backend/service/api_plan_service.go | 102 ++++++++ backend/service/api_service.go | 54 ---- backend/service/dataStore.go | 19 ++ frontend/package-lock.json | 46 ++++ frontend/package.json | 2 + frontend/src/app.css | 2 +- .../src/assets/ellipsis-vertical-solid.svg | 1 + frontend/src/assets/mountain-sun-solid.svg | 1 + frontend/src/components/Body.svelte | 2 +- frontend/src/components/Main.svelte | 8 + frontend/src/components/Plan.svelte | 3 - .../src/components/inputs/InputText.svelte | 17 +- .../inputs/MultiItemTextInput.svelte | 6 +- .../src/components/journal/Journal.svelte | 17 +- frontend/src/components/plan/Plan.svelte | 157 ++++++++++++ .../src/components/plan/PlanDndList.svelte | 87 +++++++ frontend/src/components/plan/PlanInput.svelte | 32 +++ frontend/src/components/tabs/TabBar.svelte | 11 +- .../apis/{DefaultApi.ts => JournalApi.ts} | 6 +- frontend/src/dashclient/apis/PlanApi.ts | 234 +++++++++++++++++ frontend/src/dashclient/apis/index.ts | 3 +- frontend/src/dashclient/models/PlanDay.ts | 114 +++++++++ frontend/src/dashclient/models/PlanMonth.ts | 74 ++++++ frontend/src/dashclient/models/PlanWeek.ts | 81 ++++++ .../src/dashclient/models/PlanWeekItem.ts | 82 ++++++ frontend/src/dashclient/models/index.ts | 4 + frontend/src/stores/apiStore.ts | 5 +- frontend/src/stores/appStore.ts | 3 +- frontend/src/stores/planStore.ts | 4 + 48 files changed, 2400 insertions(+), 148 deletions(-) rename backend/dashapi/{api_default.go => api_journal.go} (73%) rename backend/dashapi/{api_default_service.go => api_journal_service.go} (74%) create mode 100644 backend/dashapi/api_plan.go create mode 100644 backend/dashapi/api_plan_service.go create mode 100644 backend/dashapi/model_plan_day.go create mode 100644 backend/dashapi/model_plan_month.go create mode 100644 backend/dashapi/model_plan_week.go create mode 100644 backend/dashapi/model_plan_week_item.go delete mode 100644 backend/database/database.go create mode 100644 backend/database/models/plan.go create mode 100644 backend/service/api_journal_service.go create mode 100644 backend/service/api_plan_service.go delete mode 100644 backend/service/api_service.go create mode 100644 backend/service/dataStore.go create mode 100644 frontend/src/assets/ellipsis-vertical-solid.svg create mode 100644 frontend/src/assets/mountain-sun-solid.svg delete mode 100644 frontend/src/components/Plan.svelte create mode 100644 frontend/src/components/plan/Plan.svelte create mode 100644 frontend/src/components/plan/PlanDndList.svelte create mode 100644 frontend/src/components/plan/PlanInput.svelte rename frontend/src/dashclient/apis/{DefaultApi.ts => JournalApi.ts} (95%) create mode 100644 frontend/src/dashclient/apis/PlanApi.ts create mode 100644 frontend/src/dashclient/models/PlanDay.ts create mode 100644 frontend/src/dashclient/models/PlanMonth.ts create mode 100644 frontend/src/dashclient/models/PlanWeek.ts create mode 100644 frontend/src/dashclient/models/PlanWeekItem.ts create mode 100644 frontend/src/stores/planStore.ts diff --git a/api/dash_api.yaml b/api/dash_api.yaml index 28447bb..45deddc 100644 --- a/api/dash_api.yaml +++ b/api/dash_api.yaml @@ -1,15 +1,20 @@ openapi: '3.0.2' + info: title: Dash API version: '0.1' + servers: - url: http://localhost:18010/api/v1 - url: http://localhost:8080/api/v1 - url: https://dash.pander.me/api/v1 + paths: /journal/entry/: post: operationId: writeJournalEntry + tags: + - journal requestBody: required: true content: @@ -22,9 +27,11 @@ paths: /journal/entry/{date}: get: operationId: getJournalEntryForDate + tags: + - journal responses: '200': - description: A journal entry for a specific day. + description: A journal entry for a specific date. content: application/json: schema: @@ -39,6 +46,8 @@ paths: example: 2022-02-18 delete: operationId: deleteJournalEntryForDate + tags: + - journal responses: '200': description: Journal entry successfully deleted. @@ -50,6 +59,112 @@ paths: type: string format: date example: 2022-02-18 + + /plan/day/entry/: + post: + operationId: savePlanForDay + tags: + - plan + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/PlanDay' + responses: + '200': + description: Plan successfully written. + /plan/day/entry/{date}: + get: + operationId: getPlanDayForDate + tags: + - plan + responses: + '200': + description: A plan for a specific date. + content: + application/json: + schema: + $ref: '#/components/schemas/PlanDay' + parameters: + - name: date + in: path + required: true + schema: + type: string + format: date + example: 2022-02-18 + + /plan/week/entry/: + post: + operationId: savePlanForWeek + tags: + - plan + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/PlanWeek' + responses: + '200': + description: Plan successfully written. + /plan/week/entry/{date}: + get: + operationId: getPlanWeekForDate + tags: + - plan + responses: + '200': + description: A plan for a specific week. + content: + application/json: + schema: + $ref: '#/components/schemas/PlanWeek' + parameters: + - name: date + in: path + required: true + schema: + type: string + format: date + example: 2022-02-18 + + /plan/month/entry/: + post: + operationId: savePlanForMonth + tags: + - plan + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/PlanMonth' + responses: + '200': + description: Plan successfully written. + /plan/month/entry/{date}: + get: + operationId: getPlanMonthForDate + tags: + - plan + responses: + '200': + description: A plan for a specific week. + content: + application/json: + schema: + $ref: '#/components/schemas/PlanMonth' + parameters: + - name: date + in: path + required: true + schema: + type: string + format: date + example: 2022-02-18 + components: schemas: JournalEntry: @@ -95,4 +210,109 @@ components: - "do better 3" journal: type: string - example: "journal entry 1" \ No newline at end of file + example: "journal entry 1" + + PlanDay: + type: object + required: + - date + properties: + date: + type: string + format: date + example: 2022-02-18 + morning: + type: array + items: + type: string + example: + - "morning 1" + - "morning 2" + - "morning 3" + midday: + type: array + items: + type: string + example: + - "midday 1" + - "midday 2" + - "midday 3" + afternoon: + type: array + items: + type: string + example: + - "afternoon 1" + - "afternoon 2" + - "afternoon 3" + evening: + type: array + items: + type: string + example: + - "evening 1" + - "evening 2" + - "evening 3" + pleasant: + type: array + items: + type: string + example: + - "pleasant 1" + - "pleasant 2" + - "pleasant 3" + reminders: + type: array + items: + type: string + example: + - "reminders 1" + - "reminders 2" + - "reminders 3" + + PlanWeek: + type: object + required: + - date + properties: + date: + type: string + format: date + example: 2022-02-18 + items: + type: array + items: + $ref: '#/components/schemas/PlanWeekItem' + example: '[{"item": "witem 1"}, {"item": "witem 2", "numTodos":3, "numDone":2}, {"item": "witem 3", "numTodos":2, "numDone":2}]' + + PlanWeekItem: + type: object + required: + - item + properties: + item: + type: string + example: 'planWeekItem' + numTodos: + type: integer + format: int32 + example: 3 + numDone: + type: integer + format: int32 + example: 2 + + PlanMonth: + type: object + required: + - date + properties: + date: + type: string + format: date + example: 2022-02-18 + items: + type: array + items: + type: string + example: '["mitem1", "mitem2", "mitem3"]' \ No newline at end of file diff --git a/api/generate_go_server.sh b/api/generate_go_server.sh index 9f5f2e2..737b297 100755 --- a/api/generate_go_server.sh +++ b/api/generate_go_server.sh @@ -13,7 +13,6 @@ docker run --rm -v "${PWD}:/local" openapitools/openapi-generator-cli generate \ -g go-server \ -p packageName=dashapi,featureCORS=true,outputAsLibrary=true,sourceFolder=dashapi \ -o /local/api_server - # -p packageName=dashapi,featureCORS=true,outputAsLibrary=true \ rm -rf ../backend/dashapi cp -r ./api_server/dashapi ../backend/ diff --git a/backend/dash_backend.go b/backend/dash_backend.go index e5f139a..059b372 100644 --- a/backend/dash_backend.go +++ b/backend/dash_backend.go @@ -28,8 +28,11 @@ func main() { mapper := mapping.NewMapperImpl() - DefaultApiService := service.NewApiService(db, mapper) - DefaultApiController := dashapi.NewDefaultApiController(DefaultApiService) + JournalApiService := service.NewJournalApiService(db, mapper) + JournalApiController := dashapi.NewJournalApiController(JournalApiService) + + PlanApiService := service.NewPlanApiService(db, mapper) + PlanApiController := dashapi.NewPlanApiController(PlanApiService) cors := handlers.CORS( // handlers.AllowedMethods([]string{"GET", "POST", "DELETE"}), @@ -37,7 +40,7 @@ func main() { handlers.AllowedOrigins([]string{"*"}), ) - router := dashapi.NewRouter(DefaultApiController) + router := dashapi.NewRouter(JournalApiController, PlanApiController) router.Methods("GET", "POST", "DELETE", "OPTIONS") log.Printf("Starting server.") diff --git a/backend/dashapi/api.go b/backend/dashapi/api.go index f7f7547..79ffb99 100644 --- a/backend/dashapi/api.go +++ b/backend/dashapi/api.go @@ -16,22 +16,47 @@ import ( -// DefaultApiRouter defines the required methods for binding the api requests to a responses for the DefaultApi -// The DefaultApiRouter implementation should parse necessary information from the http request, -// pass the data to a DefaultApiServicer to perform the required actions, then write the service results to the http response. -type DefaultApiRouter interface { +// JournalApiRouter defines the required methods for binding the api requests to a responses for the JournalApi +// The JournalApiRouter implementation should parse necessary information from the http request, +// pass the data to a JournalApiServicer to perform the required actions, then write the service results to the http response. +type JournalApiRouter interface { DeleteJournalEntryForDate(http.ResponseWriter, *http.Request) GetJournalEntryForDate(http.ResponseWriter, *http.Request) WriteJournalEntry(http.ResponseWriter, *http.Request) } +// PlanApiRouter defines the required methods for binding the api requests to a responses for the PlanApi +// The PlanApiRouter implementation should parse necessary information from the http request, +// pass the data to a PlanApiServicer to perform the required actions, then write the service results to the http response. +type PlanApiRouter interface { + GetPlanDayForDate(http.ResponseWriter, *http.Request) + GetPlanMonthForDate(http.ResponseWriter, *http.Request) + GetPlanWeekForDate(http.ResponseWriter, *http.Request) + SavePlanForDay(http.ResponseWriter, *http.Request) + SavePlanForMonth(http.ResponseWriter, *http.Request) + SavePlanForWeek(http.ResponseWriter, *http.Request) +} -// DefaultApiServicer defines the api actions for the DefaultApi service +// JournalApiServicer defines the api actions for the JournalApi service // This interface intended to stay up to date with the openapi yaml used to generate it, // while the service implementation can be ignored with the .openapi-generator-ignore file // and updated with the logic required for the API. -type DefaultApiServicer interface { +type JournalApiServicer interface { DeleteJournalEntryForDate(context.Context, string) (ImplResponse, error) GetJournalEntryForDate(context.Context, string) (ImplResponse, error) WriteJournalEntry(context.Context, JournalEntry) (ImplResponse, error) } + + +// PlanApiServicer defines the api actions for the PlanApi service +// This interface intended to stay up to date with the openapi yaml used to generate it, +// while the service implementation can be ignored with the .openapi-generator-ignore file +// and updated with the logic required for the API. +type PlanApiServicer interface { + GetPlanDayForDate(context.Context, string) (ImplResponse, error) + GetPlanMonthForDate(context.Context, string) (ImplResponse, error) + GetPlanWeekForDate(context.Context, string) (ImplResponse, error) + SavePlanForDay(context.Context, PlanDay) (ImplResponse, error) + SavePlanForMonth(context.Context, PlanMonth) (ImplResponse, error) + SavePlanForWeek(context.Context, PlanWeek) (ImplResponse, error) +} diff --git a/backend/dashapi/api_default.go b/backend/dashapi/api_journal.go similarity index 73% rename from backend/dashapi/api_default.go rename to backend/dashapi/api_journal.go index a814241..a5ef3ae 100644 --- a/backend/dashapi/api_default.go +++ b/backend/dashapi/api_journal.go @@ -17,25 +17,25 @@ import ( "github.com/gorilla/mux" ) -// DefaultApiController binds http requests to an api service and writes the service results to the http response -type DefaultApiController struct { - service DefaultApiServicer +// JournalApiController binds http requests to an api service and writes the service results to the http response +type JournalApiController struct { + service JournalApiServicer errorHandler ErrorHandler } -// DefaultApiOption for how the controller is set up. -type DefaultApiOption func(*DefaultApiController) +// JournalApiOption for how the controller is set up. +type JournalApiOption func(*JournalApiController) -// WithDefaultApiErrorHandler inject ErrorHandler into controller -func WithDefaultApiErrorHandler(h ErrorHandler) DefaultApiOption { - return func(c *DefaultApiController) { +// WithJournalApiErrorHandler inject ErrorHandler into controller +func WithJournalApiErrorHandler(h ErrorHandler) JournalApiOption { + return func(c *JournalApiController) { c.errorHandler = h } } -// NewDefaultApiController creates a default api controller -func NewDefaultApiController(s DefaultApiServicer, opts ...DefaultApiOption) Router { - controller := &DefaultApiController{ +// NewJournalApiController creates a default api controller +func NewJournalApiController(s JournalApiServicer, opts ...JournalApiOption) Router { + controller := &JournalApiController{ service: s, errorHandler: DefaultErrorHandler, } @@ -47,8 +47,8 @@ func NewDefaultApiController(s DefaultApiServicer, opts ...DefaultApiOption) Rou return controller } -// Routes returns all the api routes for the DefaultApiController -func (c *DefaultApiController) Routes() Routes { +// Routes returns all the api routes for the JournalApiController +func (c *JournalApiController) Routes() Routes { return Routes{ { "DeleteJournalEntryForDate", @@ -72,7 +72,7 @@ func (c *DefaultApiController) Routes() Routes { } // DeleteJournalEntryForDate - -func (c *DefaultApiController) DeleteJournalEntryForDate(w http.ResponseWriter, r *http.Request) { +func (c *JournalApiController) DeleteJournalEntryForDate(w http.ResponseWriter, r *http.Request) { params := mux.Vars(r) dateParam := params["date"] @@ -88,7 +88,7 @@ func (c *DefaultApiController) DeleteJournalEntryForDate(w http.ResponseWriter, } // GetJournalEntryForDate - -func (c *DefaultApiController) GetJournalEntryForDate(w http.ResponseWriter, r *http.Request) { +func (c *JournalApiController) GetJournalEntryForDate(w http.ResponseWriter, r *http.Request) { params := mux.Vars(r) dateParam := params["date"] @@ -104,7 +104,7 @@ func (c *DefaultApiController) GetJournalEntryForDate(w http.ResponseWriter, r * } // WriteJournalEntry - -func (c *DefaultApiController) WriteJournalEntry(w http.ResponseWriter, r *http.Request) { +func (c *JournalApiController) WriteJournalEntry(w http.ResponseWriter, r *http.Request) { journalEntryParam := JournalEntry{} d := json.NewDecoder(r.Body) d.DisallowUnknownFields() diff --git a/backend/dashapi/api_default_service.go b/backend/dashapi/api_journal_service.go similarity index 74% rename from backend/dashapi/api_default_service.go rename to backend/dashapi/api_journal_service.go index 859b899..c4cf441 100644 --- a/backend/dashapi/api_default_service.go +++ b/backend/dashapi/api_journal_service.go @@ -15,21 +15,21 @@ import ( "errors" ) -// DefaultApiService is a service that implements the logic for the DefaultApiServicer -// This service should implement the business logic for every endpoint for the DefaultApi API. +// JournalApiService is a service that implements the logic for the JournalApiServicer +// This service should implement the business logic for every endpoint for the JournalApi API. // Include any external packages or services that will be required by this service. -type DefaultApiService struct { +type JournalApiService struct { } -// NewDefaultApiService creates a default api service -func NewDefaultApiService() DefaultApiServicer { - return &DefaultApiService{} +// NewJournalApiService creates a default api service +func NewJournalApiService() JournalApiServicer { + return &JournalApiService{} } // DeleteJournalEntryForDate - -func (s *DefaultApiService) DeleteJournalEntryForDate(ctx context.Context, date string) (ImplResponse, error) { +func (s *JournalApiService) DeleteJournalEntryForDate(ctx context.Context, date string) (ImplResponse, error) { // TODO - update DeleteJournalEntryForDate with the required logic for this service method. - // Add api_default_service.go to the .openapi-generator-ignore to avoid overwriting this service implementation when updating open api generation. + // Add api_journal_service.go to the .openapi-generator-ignore to avoid overwriting this service implementation when updating open api generation. //TODO: Uncomment the next line to return response Response(200, {}) or use other options such as http.Ok ... //return Response(200, nil),nil @@ -38,9 +38,9 @@ func (s *DefaultApiService) DeleteJournalEntryForDate(ctx context.Context, date } // GetJournalEntryForDate - -func (s *DefaultApiService) GetJournalEntryForDate(ctx context.Context, date string) (ImplResponse, error) { +func (s *JournalApiService) GetJournalEntryForDate(ctx context.Context, date string) (ImplResponse, error) { // TODO - update GetJournalEntryForDate with the required logic for this service method. - // Add api_default_service.go to the .openapi-generator-ignore to avoid overwriting this service implementation when updating open api generation. + // Add api_journal_service.go to the .openapi-generator-ignore to avoid overwriting this service implementation when updating open api generation. //TODO: Uncomment the next line to return response Response(200, JournalEntry{}) or use other options such as http.Ok ... //return Response(200, JournalEntry{}), nil @@ -49,9 +49,9 @@ func (s *DefaultApiService) GetJournalEntryForDate(ctx context.Context, date str } // WriteJournalEntry - -func (s *DefaultApiService) WriteJournalEntry(ctx context.Context, journalEntry JournalEntry) (ImplResponse, error) { +func (s *JournalApiService) WriteJournalEntry(ctx context.Context, journalEntry JournalEntry) (ImplResponse, error) { // TODO - update WriteJournalEntry with the required logic for this service method. - // Add api_default_service.go to the .openapi-generator-ignore to avoid overwriting this service implementation when updating open api generation. + // Add api_journal_service.go to the .openapi-generator-ignore to avoid overwriting this service implementation when updating open api generation. //TODO: Uncomment the next line to return response Response(200, {}) or use other options such as http.Ok ... //return Response(200, nil),nil diff --git a/backend/dashapi/api_plan.go b/backend/dashapi/api_plan.go new file mode 100644 index 0000000..72b0f88 --- /dev/null +++ b/backend/dashapi/api_plan.go @@ -0,0 +1,210 @@ +/* + * Dash API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * API version: 0.1 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package dashapi + +import ( + "encoding/json" + "net/http" + "strings" + + "github.com/gorilla/mux" +) + +// PlanApiController binds http requests to an api service and writes the service results to the http response +type PlanApiController struct { + service PlanApiServicer + errorHandler ErrorHandler +} + +// PlanApiOption for how the controller is set up. +type PlanApiOption func(*PlanApiController) + +// WithPlanApiErrorHandler inject ErrorHandler into controller +func WithPlanApiErrorHandler(h ErrorHandler) PlanApiOption { + return func(c *PlanApiController) { + c.errorHandler = h + } +} + +// NewPlanApiController creates a default api controller +func NewPlanApiController(s PlanApiServicer, opts ...PlanApiOption) Router { + controller := &PlanApiController{ + service: s, + errorHandler: DefaultErrorHandler, + } + + for _, opt := range opts { + opt(controller) + } + + return controller +} + +// Routes returns all the api routes for the PlanApiController +func (c *PlanApiController) Routes() Routes { + return Routes{ + { + "GetPlanDayForDate", + strings.ToUpper("Get"), + "/api/v1/plan/day/entry/{date}", + c.GetPlanDayForDate, + }, + { + "GetPlanMonthForDate", + strings.ToUpper("Get"), + "/api/v1/plan/month/entry/{date}", + c.GetPlanMonthForDate, + }, + { + "GetPlanWeekForDate", + strings.ToUpper("Get"), + "/api/v1/plan/week/entry/{date}", + c.GetPlanWeekForDate, + }, + { + "SavePlanForDay", + strings.ToUpper("Post"), + "/api/v1/plan/day/entry/", + c.SavePlanForDay, + }, + { + "SavePlanForMonth", + strings.ToUpper("Post"), + "/api/v1/plan/month/entry/", + c.SavePlanForMonth, + }, + { + "SavePlanForWeek", + strings.ToUpper("Post"), + "/api/v1/plan/week/entry/", + c.SavePlanForWeek, + }, + } +} + +// GetPlanDayForDate - +func (c *PlanApiController) GetPlanDayForDate(w http.ResponseWriter, r *http.Request) { + params := mux.Vars(r) + dateParam := params["date"] + + result, err := c.service.GetPlanDayForDate(r.Context(), dateParam) + // If an error occurred, encode the error with the status code + if err != nil { + c.errorHandler(w, r, err, &result) + return + } + // If no error, encode the body and the result code + EncodeJSONResponse(result.Body, &result.Code, w) + +} + +// GetPlanMonthForDate - +func (c *PlanApiController) GetPlanMonthForDate(w http.ResponseWriter, r *http.Request) { + params := mux.Vars(r) + dateParam := params["date"] + + result, err := c.service.GetPlanMonthForDate(r.Context(), dateParam) + // If an error occurred, encode the error with the status code + if err != nil { + c.errorHandler(w, r, err, &result) + return + } + // If no error, encode the body and the result code + EncodeJSONResponse(result.Body, &result.Code, w) + +} + +// GetPlanWeekForDate - +func (c *PlanApiController) GetPlanWeekForDate(w http.ResponseWriter, r *http.Request) { + params := mux.Vars(r) + dateParam := params["date"] + + result, err := c.service.GetPlanWeekForDate(r.Context(), dateParam) + // If an error occurred, encode the error with the status code + if err != nil { + c.errorHandler(w, r, err, &result) + return + } + // If no error, encode the body and the result code + EncodeJSONResponse(result.Body, &result.Code, w) + +} + +// SavePlanForDay - +func (c *PlanApiController) SavePlanForDay(w http.ResponseWriter, r *http.Request) { + planDayParam := PlanDay{} + d := json.NewDecoder(r.Body) + d.DisallowUnknownFields() + if err := d.Decode(&planDayParam); err != nil { + c.errorHandler(w, r, &ParsingError{Err: err}, nil) + return + } + if err := AssertPlanDayRequired(planDayParam); err != nil { + c.errorHandler(w, r, err, nil) + return + } + result, err := c.service.SavePlanForDay(r.Context(), planDayParam) + // If an error occurred, encode the error with the status code + if err != nil { + c.errorHandler(w, r, err, &result) + return + } + // If no error, encode the body and the result code + EncodeJSONResponse(result.Body, &result.Code, w) + +} + +// SavePlanForMonth - +func (c *PlanApiController) SavePlanForMonth(w http.ResponseWriter, r *http.Request) { + planMonthParam := PlanMonth{} + d := json.NewDecoder(r.Body) + d.DisallowUnknownFields() + if err := d.Decode(&planMonthParam); err != nil { + c.errorHandler(w, r, &ParsingError{Err: err}, nil) + return + } + if err := AssertPlanMonthRequired(planMonthParam); err != nil { + c.errorHandler(w, r, err, nil) + return + } + result, err := c.service.SavePlanForMonth(r.Context(), planMonthParam) + // If an error occurred, encode the error with the status code + if err != nil { + c.errorHandler(w, r, err, &result) + return + } + // If no error, encode the body and the result code + EncodeJSONResponse(result.Body, &result.Code, w) + +} + +// SavePlanForWeek - +func (c *PlanApiController) SavePlanForWeek(w http.ResponseWriter, r *http.Request) { + planWeekParam := PlanWeek{} + d := json.NewDecoder(r.Body) + d.DisallowUnknownFields() + if err := d.Decode(&planWeekParam); err != nil { + c.errorHandler(w, r, &ParsingError{Err: err}, nil) + return + } + if err := AssertPlanWeekRequired(planWeekParam); err != nil { + c.errorHandler(w, r, err, nil) + return + } + result, err := c.service.SavePlanForWeek(r.Context(), planWeekParam) + // If an error occurred, encode the error with the status code + if err != nil { + c.errorHandler(w, r, err, &result) + return + } + // If no error, encode the body and the result code + EncodeJSONResponse(result.Body, &result.Code, w) + +} diff --git a/backend/dashapi/api_plan_service.go b/backend/dashapi/api_plan_service.go new file mode 100644 index 0000000..6058e0b --- /dev/null +++ b/backend/dashapi/api_plan_service.go @@ -0,0 +1,93 @@ +/* + * Dash API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * API version: 0.1 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package dashapi + +import ( + "context" + "net/http" + "errors" +) + +// PlanApiService is a service that implements the logic for the PlanApiServicer +// This service should implement the business logic for every endpoint for the PlanApi API. +// Include any external packages or services that will be required by this service. +type PlanApiService struct { +} + +// NewPlanApiService creates a default api service +func NewPlanApiService() PlanApiServicer { + return &PlanApiService{} +} + +// GetPlanDayForDate - +func (s *PlanApiService) GetPlanDayForDate(ctx context.Context, date string) (ImplResponse, error) { + // TODO - update GetPlanDayForDate with the required logic for this service method. + // Add api_plan_service.go to the .openapi-generator-ignore to avoid overwriting this service implementation when updating open api generation. + + //TODO: Uncomment the next line to return response Response(200, PlanDay{}) or use other options such as http.Ok ... + //return Response(200, PlanDay{}), nil + + return Response(http.StatusNotImplemented, nil), errors.New("GetPlanDayForDate method not implemented") +} + +// GetPlanMonthForDate - +func (s *PlanApiService) GetPlanMonthForDate(ctx context.Context, date string) (ImplResponse, error) { + // TODO - update GetPlanMonthForDate with the required logic for this service method. + // Add api_plan_service.go to the .openapi-generator-ignore to avoid overwriting this service implementation when updating open api generation. + + //TODO: Uncomment the next line to return response Response(200, PlanMonth{}) or use other options such as http.Ok ... + //return Response(200, PlanMonth{}), nil + + return Response(http.StatusNotImplemented, nil), errors.New("GetPlanMonthForDate method not implemented") +} + +// GetPlanWeekForDate - +func (s *PlanApiService) GetPlanWeekForDate(ctx context.Context, date string) (ImplResponse, error) { + // TODO - update GetPlanWeekForDate with the required logic for this service method. + // Add api_plan_service.go to the .openapi-generator-ignore to avoid overwriting this service implementation when updating open api generation. + + //TODO: Uncomment the next line to return response Response(200, PlanWeek{}) or use other options such as http.Ok ... + //return Response(200, PlanWeek{}), nil + + return Response(http.StatusNotImplemented, nil), errors.New("GetPlanWeekForDate method not implemented") +} + +// SavePlanForDay - +func (s *PlanApiService) SavePlanForDay(ctx context.Context, planDay PlanDay) (ImplResponse, error) { + // TODO - update SavePlanForDay with the required logic for this service method. + // Add api_plan_service.go to the .openapi-generator-ignore to avoid overwriting this service implementation when updating open api generation. + + //TODO: Uncomment the next line to return response Response(200, {}) or use other options such as http.Ok ... + //return Response(200, nil),nil + + return Response(http.StatusNotImplemented, nil), errors.New("SavePlanForDay method not implemented") +} + +// SavePlanForMonth - +func (s *PlanApiService) SavePlanForMonth(ctx context.Context, planMonth PlanMonth) (ImplResponse, error) { + // TODO - update SavePlanForMonth with the required logic for this service method. + // Add api_plan_service.go to the .openapi-generator-ignore to avoid overwriting this service implementation when updating open api generation. + + //TODO: Uncomment the next line to return response Response(200, {}) or use other options such as http.Ok ... + //return Response(200, nil),nil + + return Response(http.StatusNotImplemented, nil), errors.New("SavePlanForMonth method not implemented") +} + +// SavePlanForWeek - +func (s *PlanApiService) SavePlanForWeek(ctx context.Context, planWeek PlanWeek) (ImplResponse, error) { + // TODO - update SavePlanForWeek with the required logic for this service method. + // Add api_plan_service.go to the .openapi-generator-ignore to avoid overwriting this service implementation when updating open api generation. + + //TODO: Uncomment the next line to return response Response(200, {}) or use other options such as http.Ok ... + //return Response(200, nil),nil + + return Response(http.StatusNotImplemented, nil), errors.New("SavePlanForWeek method not implemented") +} diff --git a/backend/dashapi/model_plan_day.go b/backend/dashapi/model_plan_day.go new file mode 100644 index 0000000..02d902a --- /dev/null +++ b/backend/dashapi/model_plan_day.go @@ -0,0 +1,53 @@ +/* + * Dash API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * API version: 0.1 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package dashapi + +type PlanDay struct { + + Date string `json:"date"` + + Morning []string `json:"morning,omitempty"` + + Midday []string `json:"midday,omitempty"` + + Afternoon []string `json:"afternoon,omitempty"` + + Evening []string `json:"evening,omitempty"` + + Pleasant []string `json:"pleasant,omitempty"` + + Reminders []string `json:"reminders,omitempty"` +} + +// AssertPlanDayRequired checks if the required fields are not zero-ed +func AssertPlanDayRequired(obj PlanDay) error { + elements := map[string]interface{}{ + "date": obj.Date, + } + for name, el := range elements { + if isZero := IsZeroValue(el); isZero { + return &RequiredError{Field: name} + } + } + + return nil +} + +// AssertRecursePlanDayRequired recursively checks if required fields are not zero-ed in a nested slice. +// Accepts only nested slice of PlanDay (e.g. [][]PlanDay), otherwise ErrTypeAssertionError is thrown. +func AssertRecursePlanDayRequired(objSlice interface{}) error { + return AssertRecurseInterfaceRequired(objSlice, func(obj interface{}) error { + aPlanDay, ok := obj.(PlanDay) + if !ok { + return ErrTypeAssertionError + } + return AssertPlanDayRequired(aPlanDay) + }) +} diff --git a/backend/dashapi/model_plan_month.go b/backend/dashapi/model_plan_month.go new file mode 100644 index 0000000..f279c69 --- /dev/null +++ b/backend/dashapi/model_plan_month.go @@ -0,0 +1,43 @@ +/* + * Dash API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * API version: 0.1 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package dashapi + +type PlanMonth struct { + + Date string `json:"date"` + + Items []string `json:"items,omitempty"` +} + +// AssertPlanMonthRequired checks if the required fields are not zero-ed +func AssertPlanMonthRequired(obj PlanMonth) error { + elements := map[string]interface{}{ + "date": obj.Date, + } + for name, el := range elements { + if isZero := IsZeroValue(el); isZero { + return &RequiredError{Field: name} + } + } + + return nil +} + +// AssertRecursePlanMonthRequired recursively checks if required fields are not zero-ed in a nested slice. +// Accepts only nested slice of PlanMonth (e.g. [][]PlanMonth), otherwise ErrTypeAssertionError is thrown. +func AssertRecursePlanMonthRequired(objSlice interface{}) error { + return AssertRecurseInterfaceRequired(objSlice, func(obj interface{}) error { + aPlanMonth, ok := obj.(PlanMonth) + if !ok { + return ErrTypeAssertionError + } + return AssertPlanMonthRequired(aPlanMonth) + }) +} diff --git a/backend/dashapi/model_plan_week.go b/backend/dashapi/model_plan_week.go new file mode 100644 index 0000000..3ece7ea --- /dev/null +++ b/backend/dashapi/model_plan_week.go @@ -0,0 +1,48 @@ +/* + * Dash API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * API version: 0.1 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package dashapi + +type PlanWeek struct { + + Date string `json:"date"` + + Items []PlanWeekItem `json:"items,omitempty"` +} + +// AssertPlanWeekRequired checks if the required fields are not zero-ed +func AssertPlanWeekRequired(obj PlanWeek) error { + elements := map[string]interface{}{ + "date": obj.Date, + } + for name, el := range elements { + if isZero := IsZeroValue(el); isZero { + return &RequiredError{Field: name} + } + } + + for _, el := range obj.Items { + if err := AssertPlanWeekItemRequired(el); err != nil { + return err + } + } + return nil +} + +// AssertRecursePlanWeekRequired recursively checks if required fields are not zero-ed in a nested slice. +// Accepts only nested slice of PlanWeek (e.g. [][]PlanWeek), otherwise ErrTypeAssertionError is thrown. +func AssertRecursePlanWeekRequired(objSlice interface{}) error { + return AssertRecurseInterfaceRequired(objSlice, func(obj interface{}) error { + aPlanWeek, ok := obj.(PlanWeek) + if !ok { + return ErrTypeAssertionError + } + return AssertPlanWeekRequired(aPlanWeek) + }) +} diff --git a/backend/dashapi/model_plan_week_item.go b/backend/dashapi/model_plan_week_item.go new file mode 100644 index 0000000..ba22097 --- /dev/null +++ b/backend/dashapi/model_plan_week_item.go @@ -0,0 +1,45 @@ +/* + * Dash API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * API version: 0.1 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package dashapi + +type PlanWeekItem struct { + + Item string `json:"item"` + + NumTodos int32 `json:"numTodos,omitempty"` + + NumDone int32 `json:"numDone,omitempty"` +} + +// AssertPlanWeekItemRequired checks if the required fields are not zero-ed +func AssertPlanWeekItemRequired(obj PlanWeekItem) error { + elements := map[string]interface{}{ + "item": obj.Item, + } + for name, el := range elements { + if isZero := IsZeroValue(el); isZero { + return &RequiredError{Field: name} + } + } + + return nil +} + +// AssertRecursePlanWeekItemRequired recursively checks if required fields are not zero-ed in a nested slice. +// Accepts only nested slice of PlanWeekItem (e.g. [][]PlanWeekItem), otherwise ErrTypeAssertionError is thrown. +func AssertRecursePlanWeekItemRequired(objSlice interface{}) error { + return AssertRecurseInterfaceRequired(objSlice, func(obj interface{}) error { + aPlanWeekItem, ok := obj.(PlanWeekItem) + if !ok { + return ErrTypeAssertionError + } + return AssertPlanWeekItemRequired(aPlanWeekItem) + }) +} diff --git a/backend/database/database.go b/backend/database/database.go deleted file mode 100644 index c3727d4..0000000 --- a/backend/database/database.go +++ /dev/null @@ -1,11 +0,0 @@ -package database - -import ( - "time" - // "github.com/moustachioed/dash/backend/database/models" -) - -type Database interface { - WriteJournalEntry(interface{}) error - GetJournalEntryForDate(time.Time) (interface{}, error) -} diff --git a/backend/database/models/plan.go b/backend/database/models/plan.go new file mode 100644 index 0000000..705a576 --- /dev/null +++ b/backend/database/models/plan.go @@ -0,0 +1,25 @@ +package models + +import ( + "gorm.io/datatypes" +) + +type PlanDay struct { + Date datatypes.Date `gorm:"primaryKey"` + Morning datatypes.JSON + Midday datatypes.JSON + Afternoon datatypes.JSON + Evening datatypes.JSON + Pleasant datatypes.JSON + Reminders datatypes.JSON +} + +type PlanWeek struct { + Date datatypes.Date `gorm:"primaryKey"` + Items datatypes.JSON +} + +type PlanMonth struct { + Date datatypes.Date `gorm:"primaryKey"` + Items datatypes.JSON +} diff --git a/backend/database/pgDatabase.go b/backend/database/pgDatabase.go index 30709ba..47c5e84 100644 --- a/backend/database/pgDatabase.go +++ b/backend/database/pgDatabase.go @@ -11,13 +11,14 @@ import ( "gorm.io/gorm/clause" "github.com/moustachioed/dash/backend/database/models" + "github.com/moustachioed/dash/backend/service" ) type PgDatabase struct { Db *gorm.DB } -func NewPgDatabase(host string, user string, password string, database string, port uint16) (Database, error) { +func NewPgDatabase(host string, user string, password string, database string, port uint16) (service.DataStore, error) { db := &PgDatabase{} dsn := fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=%d sslmode=disable", host, user, password, database, port) @@ -38,6 +39,9 @@ func NewPgDatabase(host string, user string, password string, database string, p func (db *PgDatabase) migrate() { db.Db.AutoMigrate(&models.Journal{}) + db.Db.AutoMigrate(&models.PlanDay{}) + db.Db.AutoMigrate(&models.PlanWeek{}) + db.Db.AutoMigrate(&models.PlanMonth{}) } func (db *PgDatabase) WriteJournalEntry(entry interface{}) error { @@ -57,3 +61,57 @@ func (db *PgDatabase) GetJournalEntryForDate(date time.Time) (interface{}, error return entry, nil } + +func (db *PgDatabase) WritePlanDay(entry interface{}) error { + planDay := entry.(models.PlanDay) + err := db.Db.Clauses(clause.OnConflict{UpdateAll: true}).Create(&planDay).Error + if err != nil { + log.Print("Error writing plan day to database.") + return err + } + + return nil +} + +func (db *PgDatabase) GetPlanDayForDate(date time.Time) (interface{}, error) { + entry := models.PlanDay{Date: datatypes.Date(date)} + db.Db.First(&entry) + + return entry, nil +} + +func (db *PgDatabase) WritePlanWeek(entry interface{}) error { + planWeek := entry.(models.PlanWeek) + err := db.Db.Clauses(clause.OnConflict{UpdateAll: true}).Create(&planWeek).Error + if err != nil { + log.Print("Error writing plan week to database.") + return err + } + + return nil +} + +func (db *PgDatabase) GetPlanWeekForDate(date time.Time) (interface{}, error) { + entry := models.PlanWeek{Date: datatypes.Date(date)} + db.Db.First(&entry) + + return entry, nil +} + +func (db *PgDatabase) WritePlanMonth(entry interface{}) error { + planMonth := entry.(models.PlanMonth) + err := db.Db.Clauses(clause.OnConflict{UpdateAll: true}).Create(&planMonth).Error + if err != nil { + log.Print("Error writing plan month to database.") + return err + } + + return nil +} + +func (db *PgDatabase) GetPlanMonthForDate(date time.Time) (interface{}, error) { + entry := models.PlanMonth{Date: datatypes.Date(date)} + db.Db.First(&entry) + + return entry, nil +} diff --git a/backend/mapping/mapper.go b/backend/mapping/mapper.go index c3a7588..75bf1a9 100644 --- a/backend/mapping/mapper.go +++ b/backend/mapping/mapper.go @@ -2,13 +2,20 @@ package mapping import ( "time" - // api "github.com/moustachioed/dash/backend/dashapi" - // db "github.com/moustachioed/dash/backend/database/models" ) type Mapper interface { - JournalApiToDb(api interface{}) (db interface{}) - JournalDbToApi(db interface{}) (api interface{}) + JournalApiToDs(api interface{}) (db interface{}) + JournalDsToApi(db interface{}) (api interface{}) + + PlanDayApiToDs(api interface{}) (db interface{}) + PlanDayDsToApi(db interface{}) (api interface{}) + + PlanWeekApiToDs(api interface{}) (db interface{}) + PlanWeekDsToApi(db interface{}) (api interface{}) + + PlanMonthApiToDs(api interface{}) (db interface{}) + PlanMonthDsToApi(db interface{}) (api interface{}) StringToDate(string) (time.Time, error) DateToString(time.Time) string diff --git a/backend/mapping/mapperImpl.go b/backend/mapping/mapperImpl.go index 1f7abc4..d5adfe6 100644 --- a/backend/mapping/mapperImpl.go +++ b/backend/mapping/mapperImpl.go @@ -17,9 +17,9 @@ func NewMapperImpl() Mapper { return MapperImpl{} } -func (mapper MapperImpl) JournalApiToDb(am interface{}) interface{} { +func (mapper MapperImpl) JournalApiToDs(am interface{}) interface{} { apimodel := am.(api.JournalEntry) - date, err := time.Parse("2006-01-02", apimodel.Date) + date, err := mapper.StringToDate(apimodel.Date) if err != nil { log.Printf("[ERROR] Could not parse date `%s`", apimodel.Date) } @@ -54,7 +54,7 @@ func (mapper MapperImpl) JournalApiToDb(am interface{}) interface{} { } } -func (mapper MapperImpl) JournalDbToApi(dm interface{}) interface{} { +func (mapper MapperImpl) JournalDsToApi(dm interface{}) interface{} { dbmodel := dm.(db.Journal) dateValue, err := dbmodel.Date.Value() @@ -62,7 +62,8 @@ func (mapper MapperImpl) JournalDbToApi(dm interface{}) interface{} { if err != nil { date = "" } else { - date = dateValue.(time.Time).Format("2006-01-02") + // date = dateValue.(time.Time).Format("2006-01-02") + date = mapper.DateToString(dateValue.(time.Time)) } var thankful []string @@ -74,19 +75,19 @@ func (mapper MapperImpl) JournalDbToApi(dm interface{}) interface{} { var lookingForward []string err = json.Unmarshal(dbmodel.LookingForward, &lookingForward) if err != nil { - thankful = nil + lookingForward = nil } var beenGreat []string err = json.Unmarshal(dbmodel.BeenGreat, &beenGreat) if err != nil { - thankful = nil + beenGreat = nil } var doBetter []string err = json.Unmarshal(dbmodel.DoBetter, &doBetter) if err != nil { - thankful = nil + doBetter = nil } return api.JournalEntry{ @@ -99,11 +100,133 @@ func (mapper MapperImpl) JournalDbToApi(dm interface{}) interface{} { } } +func (mapper MapperImpl) PlanDayApiToDs(am interface{}) interface{} { + apimodel := am.(api.PlanDay) + date, err := mapper.StringToDate(apimodel.Date) + if err != nil { + log.Printf("[ERROR] Could not parse date `%s`", apimodel.Date) + } + + morning, err := json.Marshal(apimodel.Morning) + if err != nil { + morning = nil + } + + midday, err := json.Marshal(apimodel.Midday) + if err != nil { + midday = nil + } + + afternoon, err := json.Marshal(apimodel.Afternoon) + if err != nil { + afternoon = nil + } + + evening, err := json.Marshal(apimodel.Evening) + if err != nil { + evening = nil + } + + pleasant, err := json.Marshal(apimodel.Pleasant) + if err != nil { + pleasant = nil + } + + reminders, err := json.Marshal(apimodel.Reminders) + if err != nil { + reminders = nil + } + + return db.PlanDay{ + Date: datatypes.Date(date), + Morning: datatypes.JSON(morning), + Midday: datatypes.JSON(midday), + Afternoon: datatypes.JSON(afternoon), + Evening: datatypes.JSON(evening), + Pleasant: datatypes.JSON(pleasant), + Reminders: datatypes.JSON(reminders), + } +} + +func (mapper MapperImpl) PlanDayDsToApi(dm interface{}) interface{} { + dbmodel := dm.(db.PlanDay) + + dateValue, err := dbmodel.Date.Value() + var date string + if err != nil { + date = "" + } else { + date = mapper.DateToString(dateValue.(time.Time)) + } + + var morning []string + err = json.Unmarshal(dbmodel.Morning, &morning) + if err != nil { + morning = nil + } + + var midday []string + err = json.Unmarshal(dbmodel.Midday, &midday) + if err != nil { + midday = nil + } + + var afternoon []string + err = json.Unmarshal(dbmodel.Afternoon, &afternoon) + if err != nil { + afternoon = nil + } + + var evening []string + err = json.Unmarshal(dbmodel.Evening, &evening) + if err != nil { + evening = nil + } + + var pleasant []string + err = json.Unmarshal(dbmodel.Pleasant, &pleasant) + if err != nil { + pleasant = nil + } + + var reminders []string + err = json.Unmarshal(dbmodel.Reminders, &reminders) + if err != nil { + reminders = nil + } + + return api.PlanDay{ + Date: date, + Morning: morning, + Midday: midday, + Afternoon: afternoon, + Evening: evening, + Pleasant: pleasant, + Reminders: reminders, + } +} + +func (mapper MapperImpl) PlanWeekApiToDs(api interface{}) (db interface{}) { + return new(interface{}) +} + +func (mapper MapperImpl) PlanWeekDsToApi(api interface{}) (db interface{}) { + return new(interface{}) +} + +func (mapper MapperImpl) PlanMonthApiToDs(api interface{}) (db interface{}) { + return new(interface{}) +} + +func (mapper MapperImpl) PlanMonthDsToApi(api interface{}) (db interface{}) { + return new(interface{}) +} + func (mapper MapperImpl) StringToDate(dateString string) (time.Time, error) { date, err := time.Parse("2006-01-02", dateString) if err != nil { log.Printf("[ERROR] Could not parse date string %s", dateString) - return time.Now(), err + return time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC), err } return date, nil } diff --git a/backend/mapping/mapperImpl_test.go b/backend/mapping/mapperImpl_test.go index dad9fd5..ffd46da 100644 --- a/backend/mapping/mapperImpl_test.go +++ b/backend/mapping/mapperImpl_test.go @@ -2,6 +2,7 @@ package mapping import ( "encoding/json" + "reflect" "testing" "time" @@ -22,7 +23,7 @@ func TestJournalApiToDbFullObject(t *testing.T) { Journal: "jtest", } - db := mapper.JournalApiToDb(api).(models.Journal) + db := mapper.JournalApiToDs(api).(models.Journal) gotDate, _ := db.Date.Value() got := gotDate.(time.Time).Format("2006-01-02") @@ -80,7 +81,7 @@ func TestJournalApiToDbPartialObject(t *testing.T) { Journal: "", } - db := mapper.JournalApiToDb(api).(models.Journal) + db := mapper.JournalApiToDs(api).(models.Journal) gotDate, _ := db.Date.Value() got := gotDate.(time.Time).Format("2006-01-02") @@ -145,7 +146,7 @@ func TestJournalDbToApiFullObject(t *testing.T) { Journal: journal, } - api := mapper.JournalDbToApi(db).(dashapi.JournalEntry) + api := mapper.JournalDsToApi(db).(dashapi.JournalEntry) got, _ := json.Marshal(api.Date) wantDate := "\"2022-02-18\"" @@ -209,7 +210,7 @@ func TestJournalDbToApiPartialObject(t *testing.T) { Journal: journal, } - api := mapper.JournalDbToApi(db).(dashapi.JournalEntry) + api := mapper.JournalDsToApi(db).(dashapi.JournalEntry) got, _ := json.Marshal(api.Date) wantDate := "\"2022-02-18\"" @@ -253,3 +254,229 @@ func TestJournalDbToApiPartialObject(t *testing.T) { t.Errorf("Mapped string %s not equal want string %s", gotJournal, wantJournal) } } + +func TestMapperImpl_PlanDayApiToDs(t *testing.T) { + date, _ := time.Parse("2006-01-02", "2022-02-18") + morning, _ := json.Marshal([]string{"motest1", "motest2", "motest3"}) + midday, _ := json.Marshal([]string{"mitest1", "mitest2", "mitest3"}) + afternoon, _ := json.Marshal([]string{"antest1", "antest2", "antest3"}) + evening, _ := json.Marshal([]string{"etest1", "etest2", "etest3"}) + pleasant, _ := json.Marshal([]string{"ptest1", "ptest2", "ptest3"}) + reminders, _ := json.Marshal([]string{"rtest1", "rtest2", "rtest3"}) + empty, _ := json.Marshal(nil) + + type args struct { + am interface{} + } + tests := []struct { + name string + mapper MapperImpl + args args + want interface{} + }{ + { + name: "Full Object", + mapper: MapperImpl{}, + args: args{ + am: dashapi.PlanDay{ + Date: "2022-02-18", + Morning: []string{"motest1", "motest2", "motest3"}, + Midday: []string{"mitest1", "mitest2", "mitest3"}, + Afternoon: []string{"antest1", "antest2", "antest3"}, + Evening: []string{"etest1", "etest2", "etest3"}, + Pleasant: []string{"ptest1", "ptest2", "ptest3"}, + Reminders: []string{"rtest1", "rtest2", "rtest3"}, + }}, + want: models.PlanDay{ + Date: datatypes.Date(date), + Morning: datatypes.JSON(morning), + Midday: datatypes.JSON(midday), + Afternoon: datatypes.JSON(afternoon), + Evening: datatypes.JSON(evening), + Pleasant: datatypes.JSON(pleasant), + Reminders: datatypes.JSON(reminders), + }, + }, + { + name: "Partial Object", + mapper: MapperImpl{}, + args: args{ + am: dashapi.PlanDay{ + Date: "2022-02-18", + Morning: []string{"motest1", "motest2", "motest3"}, + Midday: nil, + Afternoon: nil, + Evening: nil, + Pleasant: []string{"ptest1", "ptest2", "ptest3"}, + Reminders: nil, + }}, + want: models.PlanDay{ + Date: datatypes.Date(date), + Morning: datatypes.JSON(morning), + Midday: datatypes.JSON(empty), + Afternoon: datatypes.JSON(empty), + Evening: datatypes.JSON(empty), + Pleasant: datatypes.JSON(pleasant), + Reminders: datatypes.JSON(empty), + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mapper := MapperImpl{} + if got := mapper.PlanDayApiToDs(tt.args.am); !reflect.DeepEqual(got, tt.want) { + t.Errorf("MapperImpl.PlanDayApiToDs() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestMapperImpl_PlanDayDsToApi(t *testing.T) { + date, _ := time.Parse("2006-01-02", "2022-02-18") + morning, _ := json.Marshal([]string{"motest1", "motest2", "motest3"}) + midday, _ := json.Marshal([]string{"mitest1", "mitest2", "mitest3"}) + afternoon, _ := json.Marshal([]string{"antest1", "antest2", "antest3"}) + evening, _ := json.Marshal([]string{"etest1", "etest2", "etest3"}) + pleasant, _ := json.Marshal([]string{"ptest1", "ptest2", "ptest3"}) + reminders, _ := json.Marshal([]string{"rtest1", "rtest2", "rtest3"}) + empty, _ := json.Marshal(nil) + + type args struct { + dm interface{} + } + tests := []struct { + name string + mapper MapperImpl + args args + want interface{} + }{ + { + name: "Full Object", + mapper: MapperImpl{}, + args: args{ + dm: models.PlanDay{ + Date: datatypes.Date(date), + Morning: datatypes.JSON(morning), + Midday: datatypes.JSON(midday), + Afternoon: datatypes.JSON(afternoon), + Evening: datatypes.JSON(evening), + Pleasant: datatypes.JSON(pleasant), + Reminders: datatypes.JSON(reminders), + }}, + want: dashapi.PlanDay{ + Date: "2022-02-18", + Morning: []string{"motest1", "motest2", "motest3"}, + Midday: []string{"mitest1", "mitest2", "mitest3"}, + Afternoon: []string{"antest1", "antest2", "antest3"}, + Evening: []string{"etest1", "etest2", "etest3"}, + Pleasant: []string{"ptest1", "ptest2", "ptest3"}, + Reminders: []string{"rtest1", "rtest2", "rtest3"}, + }, + }, + { + name: "Partial Object", + mapper: MapperImpl{}, + args: args{ + dm: models.PlanDay{ + Date: datatypes.Date(date), + Morning: datatypes.JSON(morning), + Midday: datatypes.JSON(empty), + Afternoon: datatypes.JSON(afternoon), + Evening: datatypes.JSON(empty), + Pleasant: datatypes.JSON(pleasant), + Reminders: datatypes.JSON(empty), + }}, + want: dashapi.PlanDay{ + Date: "2022-02-18", + Morning: []string{"motest1", "motest2", "motest3"}, + Midday: nil, + Afternoon: []string{"antest1", "antest2", "antest3"}, + Evening: nil, + Pleasant: []string{"ptest1", "ptest2", "ptest3"}, + Reminders: nil, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mapper := MapperImpl{} + if got := mapper.PlanDayDsToApi(tt.args.dm); !reflect.DeepEqual(got, tt.want) { + t.Errorf("MapperImpl.PlanDayDsToApi() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestMapperImpl_StringToDate(t *testing.T) { + type args struct { + dateString string + } + tests := []struct { + name string + mapper MapperImpl + args args + want time.Time + wantErr bool + }{ + { + name: "Well-formed date", + mapper: MapperImpl{}, + args: args{ + dateString: "1987-02-18", + }, + want: time.Date(1987, 2, 18, 0, 0, 0, 0, time.UTC), + wantErr: false, + }, + { + name: "False string", + mapper: MapperImpl{}, + args: args{ + dateString: "1987-02-18'T'13:02:18", + }, + want: time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC), + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mapper := MapperImpl{} + got, err := mapper.StringToDate(tt.args.dateString) + if (err != nil) != tt.wantErr { + t.Errorf("MapperImpl.StringToDate() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("MapperImpl.StringToDate() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestMapperImpl_DateToString(t *testing.T) { + type args struct { + date time.Time + } + tests := []struct { + name string + mapper MapperImpl + args args + want string + }{ + { + name: "Well-formed date", + mapper: MapperImpl{}, + args: args{ + date: time.Date(1987, 2, 18, 0, 0, 0, 0, time.UTC), + }, + want: "1987-02-18", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mapper := MapperImpl{} + if got := mapper.DateToString(tt.args.date); got != tt.want { + t.Errorf("MapperImpl.DateToString() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/backend/service/api_journal_service.go b/backend/service/api_journal_service.go new file mode 100644 index 0000000..14d3367 --- /dev/null +++ b/backend/service/api_journal_service.go @@ -0,0 +1,67 @@ +/* + * Dash API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * API version: 0.1 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package service + +import ( + "context" + "errors" + "log" + "net/http" + + "github.com/moustachioed/dash/backend/dashapi" + "github.com/moustachioed/dash/backend/mapping" +) + +// JournalApiService is a service that implements the logic for the JournalApiServicer +type JournalApiService struct { + ds DataStore + mapper mapping.Mapper +} + +// NewJournalApiService creates a default api service +func NewJournalApiService(ds DataStore, mapper mapping.Mapper) dashapi.JournalApiServicer { + service := JournalApiService{ + ds: ds, + mapper: mapper, + } + + return &service +} + +// DeleteJournalEntryForDate - +func (s *JournalApiService) DeleteJournalEntryForDate(ctx context.Context, date string) (dashapi.ImplResponse, error) { + // TODO - update DeleteJournalEntryForDate with the required logic for this service method. + // Add api_journal_service.go to the .openapi-generator-ignore to avoid overwriting this service implementation when updating open api generation. + + //TODO: Uncomment the next line to return response Response(200, {}) or use other options such as http.Ok ... + //return Response(200, nil),nil + + return dashapi.Response(http.StatusNotImplemented, nil), errors.New("DeleteJournalEntryForDate method not implemented") +} + +// GetJournalEntryForDate - +func (s *JournalApiService) GetJournalEntryForDate(ctx context.Context, date string) (dashapi.ImplResponse, error) { + d, err := s.mapper.StringToDate(date) + if err != nil { + log.Fatal(err) + } + + dbEntry, _ := s.ds.GetJournalEntryForDate(d) + journal := s.mapper.JournalDsToApi(dbEntry) + return dashapi.Response(200, journal), nil +} + +// WriteJournalEntry - +func (s *JournalApiService) WriteJournalEntry(ctx context.Context, journalEntry dashapi.JournalEntry) (dashapi.ImplResponse, error) { + journal := s.mapper.JournalApiToDs(journalEntry) + s.ds.WriteJournalEntry(journal) + + return dashapi.Response(200, nil), nil +} diff --git a/backend/service/api_plan_service.go b/backend/service/api_plan_service.go new file mode 100644 index 0000000..3adc20c --- /dev/null +++ b/backend/service/api_plan_service.go @@ -0,0 +1,102 @@ +/* + * Dash API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * API version: 0.1 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package service + +import ( + "context" + "errors" + "log" + "net/http" + + "github.com/moustachioed/dash/backend/dashapi" + "github.com/moustachioed/dash/backend/mapping" +) + +// PlanApiService is a service that implements the logic for the PlanApiServicer +// This service should implement the business logic for every endpoint for the PlanApi API. +// Include any external packages or services that will be required by this service. +type PlanApiService struct { + ds DataStore + mapper mapping.Mapper +} + +// NewPlanApiService creates a default api service +func NewPlanApiService(ds DataStore, mapper mapping.Mapper) dashapi.PlanApiServicer { + service := PlanApiService{ + ds: ds, + mapper: mapper, + } + + return &service +} + +// GetPlanDayForDate - +func (s *PlanApiService) GetPlanDayForDate(ctx context.Context, date string) (dashapi.ImplResponse, error) { + d, err := s.mapper.StringToDate(date) + if err != nil { + log.Fatal(err) + } + + dbEntry, _ := s.ds.GetPlanDayForDate(d) + planDay := s.mapper.PlanDayDsToApi(dbEntry) + return dashapi.Response(200, planDay), nil +} + +// GetPlanMonthForDate - +func (s *PlanApiService) GetPlanMonthForDate(ctx context.Context, date string) (dashapi.ImplResponse, error) { + // TODO - update GetPlanMonthForDate with the required logic for this service method. + // Add api_plan_service.go to the .openapi-generator-ignore to avoid overwriting this service implementation when updating open api generation. + + //TODO: Uncomment the next line to return response Response(200, PlanMonth{}) or use other options such as http.Ok ... + //return Response(200, PlanMonth{}), nil + + return dashapi.Response(http.StatusNotImplemented, nil), errors.New("GetPlanMonthForDate method not implemented") +} + +// GetPlanWeekForDate - +func (s *PlanApiService) GetPlanWeekForDate(ctx context.Context, date string) (dashapi.ImplResponse, error) { + // TODO - update GetPlanWeekForDate with the required logic for this service method. + // Add api_plan_service.go to the .openapi-generator-ignore to avoid overwriting this service implementation when updating open api generation. + + //TODO: Uncomment the next line to return response Response(200, PlanWeek{}) or use other options such as http.Ok ... + //return Response(200, PlanWeek{}), nil + + return dashapi.Response(http.StatusNotImplemented, nil), errors.New("GetPlanWeekForDate method not implemented") +} + +// SavePlanForDay - +func (s *PlanApiService) SavePlanForDay(ctx context.Context, planDay dashapi.PlanDay) (dashapi.ImplResponse, error) { + plan := s.mapper.PlanDayApiToDs(planDay) + s.ds.WritePlanDay(plan) + + return dashapi.Response(200, nil), nil +} + +// SavePlanForMonth - +func (s *PlanApiService) SavePlanForMonth(ctx context.Context, planMonth dashapi.PlanMonth) (dashapi.ImplResponse, error) { + // TODO - update SavePlanForMonth with the required logic for this service method. + // Add api_plan_service.go to the .openapi-generator-ignore to avoid overwriting this service implementation when updating open api generation. + + //TODO: Uncomment the next line to return response Response(200, {}) or use other options such as http.Ok ... + //return Response(200, nil),nil + + return dashapi.Response(http.StatusNotImplemented, nil), errors.New("SavePlanForMonth method not implemented") +} + +// SavePlanForWeek - +func (s *PlanApiService) SavePlanForWeek(ctx context.Context, planWeek dashapi.PlanWeek) (dashapi.ImplResponse, error) { + // TODO - update SavePlanForWeek with the required logic for this service method. + // Add api_plan_service.go to the .openapi-generator-ignore to avoid overwriting this service implementation when updating open api generation. + + //TODO: Uncomment the next line to return response Response(200, {}) or use other options such as http.Ok ... + //return Response(200, nil),nil + + return dashapi.Response(http.StatusNotImplemented, nil), errors.New("SavePlanForWeek method not implemented") +} diff --git a/backend/service/api_service.go b/backend/service/api_service.go deleted file mode 100644 index 62bd1ee..0000000 --- a/backend/service/api_service.go +++ /dev/null @@ -1,54 +0,0 @@ -package service - -import ( - "context" - "errors" - "log" - "net/http" - - "github.com/moustachioed/dash/backend/dashapi" - "github.com/moustachioed/dash/backend/database" - "github.com/moustachioed/dash/backend/mapping" -) - -// ApiService is a service that implements the logic for the DefaultApiServicer -// This service should implement the business logic for every endpoint for the Api API. -// Include any external packages or services that will be required by this service. -type ApiService struct { - db database.Database - mapper mapping.Mapper -} - -// NewApiService creates a default api service -func NewApiService(db database.Database, mapper mapping.Mapper) dashapi.DefaultApiServicer { - service := ApiService{ - db: db, - mapper: mapper, - } - - return &service -} - -// GetJournalEntryForDate - -func (s *ApiService) GetJournalEntryForDate(ctx context.Context, date string) (dashapi.ImplResponse, error) { - d, err := s.mapper.StringToDate(date) - if err != nil { - log.Fatal(err) - } - - dbEntry, _ := s.db.GetJournalEntryForDate(d) - journal := s.mapper.JournalDbToApi(dbEntry) - return dashapi.Response(200, journal), nil -} - -// WriteJournalEntryForDate - -func (s *ApiService) WriteJournalEntry(ctx context.Context, journalEntry dashapi.JournalEntry) (dashapi.ImplResponse, error) { - journal := s.mapper.JournalApiToDb(journalEntry) - s.db.WriteJournalEntry(journal) - - return dashapi.Response(200, nil), nil -} - -func (s *ApiService) DeleteJournalEntryForDate(ctx context.Context, date string) (dashapi.ImplResponse, error) { - return dashapi.Response(http.StatusNotImplemented, nil), errors.New("DeleteJournalEntryForDate method not implemented") -} diff --git a/backend/service/dataStore.go b/backend/service/dataStore.go new file mode 100644 index 0000000..1f51536 --- /dev/null +++ b/backend/service/dataStore.go @@ -0,0 +1,19 @@ +package service + +import ( + "time" +) + +type DataStore interface { + WriteJournalEntry(interface{}) error + GetJournalEntryForDate(time.Time) (interface{}, error) + + WritePlanDay(interface{}) error + GetPlanDayForDate(time.Time) (interface{}, error) + + WritePlanWeek(interface{}) error + GetPlanWeekForDate(time.Time) (interface{}, error) + + WritePlanMonth(interface{}) error + GetPlanMonthForDate(time.Time) (interface{}, error) +} diff --git a/frontend/package-lock.json b/frontend/package-lock.json index b135ed5..1a99425 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -10,8 +10,10 @@ "devDependencies": { "@sveltejs/vite-plugin-svelte": "^1.0.1", "@tsconfig/svelte": "^3.0.0", + "shortid": "^2.2.16", "svelte": "^3.49.0", "svelte-check": "^2.8.0", + "svelte-dnd-action": "^0.9.21", "svelte-preprocess": "^4.10.7", "tslib": "^2.4.0", "typescript": "^4.6.4", @@ -1202,6 +1204,21 @@ "rimraf": "^2.5.2" } }, + "node_modules/shortid": { + "version": "2.2.16", + "resolved": "https://registry.npmjs.org/shortid/-/shortid-2.2.16.tgz", + "integrity": "sha512-Ugt+GIZqvGXCIItnsL+lvFJOiN7RYqlGy7QE41O3YC1xbNSeDGIRO7xg2JJXIAj1cAGnOeC1r7/T9pgrtQbv4g==", + "dev": true, + "dependencies": { + "nanoid": "^2.1.0" + } + }, + "node_modules/shortid/node_modules/nanoid": { + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-2.1.11.tgz", + "integrity": "sha512-s/snB+WGm6uwi0WjsZdaVcuf3KJXlfGl2LcxgwkEwJF0D/BWzVWAZW/XY4bFaiR7s0Jk3FPvlnepg1H1b1UwlA==", + "dev": true + }, "node_modules/sorcery": { "version": "0.10.0", "resolved": "https://registry.npmjs.org/sorcery/-/sorcery-0.10.0.tgz", @@ -1287,6 +1304,12 @@ "svelte": "^3.24.0" } }, + "node_modules/svelte-dnd-action": { + "version": "0.9.21", + "resolved": "https://registry.npmjs.org/svelte-dnd-action/-/svelte-dnd-action-0.9.21.tgz", + "integrity": "sha512-uSiaGiSY5KCTx5OtColSk/1qFFUAzGvnoQYDE5xfOmcyvf3lAI1IjPv0p0LRKrTudS9rGZYWPx44qsD09ER6QQ==", + "dev": true + }, "node_modules/svelte-hmr": { "version": "0.14.12", "resolved": "https://registry.npmjs.org/svelte-hmr/-/svelte-hmr-0.14.12.tgz", @@ -2214,6 +2237,23 @@ "rimraf": "^2.5.2" } }, + "shortid": { + "version": "2.2.16", + "resolved": "https://registry.npmjs.org/shortid/-/shortid-2.2.16.tgz", + "integrity": "sha512-Ugt+GIZqvGXCIItnsL+lvFJOiN7RYqlGy7QE41O3YC1xbNSeDGIRO7xg2JJXIAj1cAGnOeC1r7/T9pgrtQbv4g==", + "dev": true, + "requires": { + "nanoid": "^2.1.0" + }, + "dependencies": { + "nanoid": { + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-2.1.11.tgz", + "integrity": "sha512-s/snB+WGm6uwi0WjsZdaVcuf3KJXlfGl2LcxgwkEwJF0D/BWzVWAZW/XY4bFaiR7s0Jk3FPvlnepg1H1b1UwlA==", + "dev": true + } + } + }, "sorcery": { "version": "0.10.0", "resolved": "https://registry.npmjs.org/sorcery/-/sorcery-0.10.0.tgz", @@ -2275,6 +2315,12 @@ "typescript": "*" } }, + "svelte-dnd-action": { + "version": "0.9.21", + "resolved": "https://registry.npmjs.org/svelte-dnd-action/-/svelte-dnd-action-0.9.21.tgz", + "integrity": "sha512-uSiaGiSY5KCTx5OtColSk/1qFFUAzGvnoQYDE5xfOmcyvf3lAI1IjPv0p0LRKrTudS9rGZYWPx44qsD09ER6QQ==", + "dev": true + }, "svelte-hmr": { "version": "0.14.12", "resolved": "https://registry.npmjs.org/svelte-hmr/-/svelte-hmr-0.14.12.tgz", diff --git a/frontend/package.json b/frontend/package.json index f11646f..c2f498f 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -12,8 +12,10 @@ "devDependencies": { "@sveltejs/vite-plugin-svelte": "^1.0.1", "@tsconfig/svelte": "^3.0.0", + "shortid": "^2.2.16", "svelte": "^3.49.0", "svelte-check": "^2.8.0", + "svelte-dnd-action": "^0.9.21", "svelte-preprocess": "^4.10.7", "tslib": "^2.4.0", "typescript": "^4.6.4", diff --git a/frontend/src/app.css b/frontend/src/app.css index d839c62..fa1033f 100644 --- a/frontend/src/app.css +++ b/frontend/src/app.css @@ -27,7 +27,7 @@ a:hover { body { margin: 0; display: flex; - place-items: center; + place-items: top; min-width: 320px; min-height: 100vh; } diff --git a/frontend/src/assets/ellipsis-vertical-solid.svg b/frontend/src/assets/ellipsis-vertical-solid.svg new file mode 100644 index 0000000..6b7526e --- /dev/null +++ b/frontend/src/assets/ellipsis-vertical-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/mountain-sun-solid.svg b/frontend/src/assets/mountain-sun-solid.svg new file mode 100644 index 0000000..2603c89 --- /dev/null +++ b/frontend/src/assets/mountain-sun-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/components/Body.svelte b/frontend/src/components/Body.svelte index db88dc0..59d444c 100644 --- a/frontend/src/components/Body.svelte +++ b/frontend/src/components/Body.svelte @@ -1,7 +1,7 @@
diff --git a/frontend/src/components/Main.svelte b/frontend/src/components/Main.svelte index e962377..9934bbc 100644 --- a/frontend/src/components/Main.svelte +++ b/frontend/src/components/Main.svelte @@ -11,5 +11,13 @@ \ No newline at end of file diff --git a/frontend/src/components/Plan.svelte b/frontend/src/components/Plan.svelte deleted file mode 100644 index f57ae09..0000000 --- a/frontend/src/components/Plan.svelte +++ /dev/null @@ -1,3 +0,0 @@ -
-

Plan

-
\ No newline at end of file diff --git a/frontend/src/components/inputs/InputText.svelte b/frontend/src/components/inputs/InputText.svelte index cf3cba1..c202e02 100644 --- a/frontend/src/components/inputs/InputText.svelte +++ b/frontend/src/components/inputs/InputText.svelte @@ -1,9 +1,20 @@ - + + \ No newline at end of file diff --git a/frontend/src/components/plan/PlanDndList.svelte b/frontend/src/components/plan/PlanDndList.svelte new file mode 100644 index 0000000..3e2a324 --- /dev/null +++ b/frontend/src/components/plan/PlanDndList.svelte @@ -0,0 +1,87 @@ + + +
+ + +
+
+ + {#each items as item(item.id)} +
+
+ +
+
+ {/each} + +
+ + \ No newline at end of file diff --git a/frontend/src/components/plan/PlanInput.svelte b/frontend/src/components/plan/PlanInput.svelte new file mode 100644 index 0000000..7638718 --- /dev/null +++ b/frontend/src/components/plan/PlanInput.svelte @@ -0,0 +1,32 @@ + + +{#if doEdit} + +{:else} +
+ {text} +
+{/if} + + \ No newline at end of file diff --git a/frontend/src/components/tabs/TabBar.svelte b/frontend/src/components/tabs/TabBar.svelte index ec28fdd..af6e2c0 100644 --- a/frontend/src/components/tabs/TabBar.svelte +++ b/frontend/src/components/tabs/TabBar.svelte @@ -14,7 +14,9 @@
- +
+ +
{clickHandler("Journal")}}/> {clickHandler("Plan")}}/> {clickHandler("Tracking")}}/> @@ -29,8 +31,13 @@ display: flex; flex-wrap: wrap; /* for horizontal aligning of child divs */ - justify-content: center; + /* justify-content: center; */ /* for vertical aligning */ align-items: center; + gap: 5px; + } + + .datePicker { + margin-right: auto; } diff --git a/frontend/src/dashclient/apis/DefaultApi.ts b/frontend/src/dashclient/apis/JournalApi.ts similarity index 95% rename from frontend/src/dashclient/apis/DefaultApi.ts rename to frontend/src/dashclient/apis/JournalApi.ts index 419e5c4..433f534 100644 --- a/frontend/src/dashclient/apis/DefaultApi.ts +++ b/frontend/src/dashclient/apis/JournalApi.ts @@ -37,7 +37,7 @@ export interface WriteJournalEntryRequest { /** * */ -export class DefaultApi extends runtime.BaseAPI { +export class JournalApi extends runtime.BaseAPI { /** */ @@ -51,7 +51,7 @@ export class DefaultApi extends runtime.BaseAPI { const headerParameters: runtime.HTTPHeaders = {}; const response = await this.request({ - path: `/journal/entry/{date}`.replace(`{${"date"}}`, encodeURIComponent(String(requestParameters.date.toISOString().substring(0,10)))), + path: `/journal/entry/{date}`.replace(`{${"date"}}`, encodeURIComponent(requestParameters.date.toISOString().substring(0,10))), method: 'DELETE', headers: headerParameters, query: queryParameters, @@ -78,7 +78,7 @@ export class DefaultApi extends runtime.BaseAPI { const headerParameters: runtime.HTTPHeaders = {}; const response = await this.request({ - path: `/journal/entry/{date}`.replace(`{${"date"}}`, encodeURIComponent(String(requestParameters.date.toISOString().substring(0,10)))), + path: `/journal/entry/{date}`.replace(`{${"date"}}`, encodeURIComponent(requestParameters.date.toISOString().substring(0,10))), method: 'GET', headers: headerParameters, query: queryParameters, diff --git a/frontend/src/dashclient/apis/PlanApi.ts b/frontend/src/dashclient/apis/PlanApi.ts new file mode 100644 index 0000000..2e25677 --- /dev/null +++ b/frontend/src/dashclient/apis/PlanApi.ts @@ -0,0 +1,234 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Dash API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +import * as runtime from '../runtime'; +import type { + PlanDay, + PlanMonth, + PlanWeek, +} from '../models'; +import { + PlanDayFromJSON, + PlanDayToJSON, + PlanMonthFromJSON, + PlanMonthToJSON, + PlanWeekFromJSON, + PlanWeekToJSON, +} from '../models'; + +export interface GetPlanDayForDateRequest { + date: Date; +} + +export interface GetPlanMonthForDateRequest { + date: Date; +} + +export interface GetPlanWeekForDateRequest { + date: Date; +} + +export interface SavePlanForDayRequest { + planDay: PlanDay; +} + +export interface SavePlanForMonthRequest { + planMonth: PlanMonth; +} + +export interface SavePlanForWeekRequest { + planWeek: PlanWeek; +} + +/** + * + */ +export class PlanApi extends runtime.BaseAPI { + + /** + */ + async getPlanDayForDateRaw(requestParameters: GetPlanDayForDateRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters.date === null || requestParameters.date === undefined) { + throw new runtime.RequiredError('date','Required parameter requestParameters.date was null or undefined when calling getPlanDayForDate.'); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + const response = await this.request({ + path: `/plan/day/entry/{date}`.replace(`{${"date"}}`, encodeURIComponent(requestParameters.date.toISOString().substring(0,10))), + method: 'GET', + headers: headerParameters, + query: queryParameters, + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => PlanDayFromJSON(jsonValue)); + } + + /** + */ + async getPlanDayForDate(requestParameters: GetPlanDayForDateRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.getPlanDayForDateRaw(requestParameters, initOverrides); + return await response.value(); + } + + /** + */ + async getPlanMonthForDateRaw(requestParameters: GetPlanMonthForDateRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters.date === null || requestParameters.date === undefined) { + throw new runtime.RequiredError('date','Required parameter requestParameters.date was null or undefined when calling getPlanMonthForDate.'); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + const response = await this.request({ + path: `/plan/month/entry/{date}`.replace(`{${"date"}}`, encodeURIComponent(requestParameters.date.toISOString().substring(0,10))), + method: 'GET', + headers: headerParameters, + query: queryParameters, + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => PlanMonthFromJSON(jsonValue)); + } + + /** + */ + async getPlanMonthForDate(requestParameters: GetPlanMonthForDateRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.getPlanMonthForDateRaw(requestParameters, initOverrides); + return await response.value(); + } + + /** + */ + async getPlanWeekForDateRaw(requestParameters: GetPlanWeekForDateRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters.date === null || requestParameters.date === undefined) { + throw new runtime.RequiredError('date','Required parameter requestParameters.date was null or undefined when calling getPlanWeekForDate.'); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + const response = await this.request({ + path: `/plan/week/entry/{date}`.replace(`{${"date"}}`, encodeURIComponent(requestParameters.date.toISOString().substring(0,10))), + method: 'GET', + headers: headerParameters, + query: queryParameters, + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => PlanWeekFromJSON(jsonValue)); + } + + /** + */ + async getPlanWeekForDate(requestParameters: GetPlanWeekForDateRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.getPlanWeekForDateRaw(requestParameters, initOverrides); + return await response.value(); + } + + /** + */ + async savePlanForDayRaw(requestParameters: SavePlanForDayRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters.planDay === null || requestParameters.planDay === undefined) { + throw new runtime.RequiredError('planDay','Required parameter requestParameters.planDay was null or undefined when calling savePlanForDay.'); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + headerParameters['Content-Type'] = 'application/json'; + + const response = await this.request({ + path: `/plan/day/entry/`, + method: 'POST', + headers: headerParameters, + query: queryParameters, + body: PlanDayToJSON(requestParameters.planDay), + }, initOverrides); + + return new runtime.VoidApiResponse(response); + } + + /** + */ + async savePlanForDay(requestParameters: SavePlanForDayRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + await this.savePlanForDayRaw(requestParameters, initOverrides); + } + + /** + */ + async savePlanForMonthRaw(requestParameters: SavePlanForMonthRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters.planMonth === null || requestParameters.planMonth === undefined) { + throw new runtime.RequiredError('planMonth','Required parameter requestParameters.planMonth was null or undefined when calling savePlanForMonth.'); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + headerParameters['Content-Type'] = 'application/json'; + + const response = await this.request({ + path: `/plan/month/entry/`, + method: 'POST', + headers: headerParameters, + query: queryParameters, + body: PlanMonthToJSON(requestParameters.planMonth), + }, initOverrides); + + return new runtime.VoidApiResponse(response); + } + + /** + */ + async savePlanForMonth(requestParameters: SavePlanForMonthRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + await this.savePlanForMonthRaw(requestParameters, initOverrides); + } + + /** + */ + async savePlanForWeekRaw(requestParameters: SavePlanForWeekRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters.planWeek === null || requestParameters.planWeek === undefined) { + throw new runtime.RequiredError('planWeek','Required parameter requestParameters.planWeek was null or undefined when calling savePlanForWeek.'); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + headerParameters['Content-Type'] = 'application/json'; + + const response = await this.request({ + path: `/plan/week/entry/`, + method: 'POST', + headers: headerParameters, + query: queryParameters, + body: PlanWeekToJSON(requestParameters.planWeek), + }, initOverrides); + + return new runtime.VoidApiResponse(response); + } + + /** + */ + async savePlanForWeek(requestParameters: SavePlanForWeekRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + await this.savePlanForWeekRaw(requestParameters, initOverrides); + } + +} diff --git a/frontend/src/dashclient/apis/index.ts b/frontend/src/dashclient/apis/index.ts index 69c44c0..23f3751 100644 --- a/frontend/src/dashclient/apis/index.ts +++ b/frontend/src/dashclient/apis/index.ts @@ -1,3 +1,4 @@ /* tslint:disable */ /* eslint-disable */ -export * from './DefaultApi'; +export * from './JournalApi'; +export * from './PlanApi'; diff --git a/frontend/src/dashclient/models/PlanDay.ts b/frontend/src/dashclient/models/PlanDay.ts new file mode 100644 index 0000000..83c2f6f --- /dev/null +++ b/frontend/src/dashclient/models/PlanDay.ts @@ -0,0 +1,114 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Dash API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { exists, mapValues } from '../runtime'; +/** + * + * @export + * @interface PlanDay + */ +export interface PlanDay { + /** + * + * @type {Date} + * @memberof PlanDay + */ + date: Date; + /** + * + * @type {Array} + * @memberof PlanDay + */ + morning?: Array; + /** + * + * @type {Array} + * @memberof PlanDay + */ + midday?: Array; + /** + * + * @type {Array} + * @memberof PlanDay + */ + afternoon?: Array; + /** + * + * @type {Array} + * @memberof PlanDay + */ + evening?: Array; + /** + * + * @type {Array} + * @memberof PlanDay + */ + pleasant?: Array; + /** + * + * @type {Array} + * @memberof PlanDay + */ + reminders?: Array; +} + +/** + * Check if a given object implements the PlanDay interface. + */ +export function instanceOfPlanDay(value: object): boolean { + let isInstance = true; + isInstance = isInstance && "date" in value; + + return isInstance; +} + +export function PlanDayFromJSON(json: any): PlanDay { + return PlanDayFromJSONTyped(json, false); +} + +export function PlanDayFromJSONTyped(json: any, ignoreDiscriminator: boolean): PlanDay { + if ((json === undefined) || (json === null)) { + return json; + } + return { + + 'date': (new Date(json['date'])), + 'morning': !exists(json, 'morning') ? undefined : json['morning'], + 'midday': !exists(json, 'midday') ? undefined : json['midday'], + 'afternoon': !exists(json, 'afternoon') ? undefined : json['afternoon'], + 'evening': !exists(json, 'evening') ? undefined : json['evening'], + 'pleasant': !exists(json, 'pleasant') ? undefined : json['pleasant'], + 'reminders': !exists(json, 'reminders') ? undefined : json['reminders'], + }; +} + +export function PlanDayToJSON(value?: PlanDay | null): any { + if (value === undefined) { + return undefined; + } + if (value === null) { + return null; + } + return { + + 'date': (value.date.toISOString().substr(0,10)), + 'morning': value.morning, + 'midday': value.midday, + 'afternoon': value.afternoon, + 'evening': value.evening, + 'pleasant': value.pleasant, + 'reminders': value.reminders, + }; +} + diff --git a/frontend/src/dashclient/models/PlanMonth.ts b/frontend/src/dashclient/models/PlanMonth.ts new file mode 100644 index 0000000..694c212 --- /dev/null +++ b/frontend/src/dashclient/models/PlanMonth.ts @@ -0,0 +1,74 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Dash API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { exists, mapValues } from '../runtime'; +/** + * + * @export + * @interface PlanMonth + */ +export interface PlanMonth { + /** + * + * @type {Date} + * @memberof PlanMonth + */ + date: Date; + /** + * + * @type {Array} + * @memberof PlanMonth + */ + items?: Array; +} + +/** + * Check if a given object implements the PlanMonth interface. + */ +export function instanceOfPlanMonth(value: object): boolean { + let isInstance = true; + isInstance = isInstance && "date" in value; + + return isInstance; +} + +export function PlanMonthFromJSON(json: any): PlanMonth { + return PlanMonthFromJSONTyped(json, false); +} + +export function PlanMonthFromJSONTyped(json: any, ignoreDiscriminator: boolean): PlanMonth { + if ((json === undefined) || (json === null)) { + return json; + } + return { + + 'date': (new Date(json['date'])), + 'items': !exists(json, 'items') ? undefined : json['items'], + }; +} + +export function PlanMonthToJSON(value?: PlanMonth | null): any { + if (value === undefined) { + return undefined; + } + if (value === null) { + return null; + } + return { + + 'date': (value.date.toISOString().substr(0,10)), + 'items': value.items, + }; +} + diff --git a/frontend/src/dashclient/models/PlanWeek.ts b/frontend/src/dashclient/models/PlanWeek.ts new file mode 100644 index 0000000..d1fd36a --- /dev/null +++ b/frontend/src/dashclient/models/PlanWeek.ts @@ -0,0 +1,81 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Dash API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { exists, mapValues } from '../runtime'; +import type { PlanWeekItem } from './PlanWeekItem'; +import { + PlanWeekItemFromJSON, + PlanWeekItemFromJSONTyped, + PlanWeekItemToJSON, +} from './PlanWeekItem'; + +/** + * + * @export + * @interface PlanWeek + */ +export interface PlanWeek { + /** + * + * @type {Date} + * @memberof PlanWeek + */ + date: Date; + /** + * + * @type {Array} + * @memberof PlanWeek + */ + items?: Array; +} + +/** + * Check if a given object implements the PlanWeek interface. + */ +export function instanceOfPlanWeek(value: object): boolean { + let isInstance = true; + isInstance = isInstance && "date" in value; + + return isInstance; +} + +export function PlanWeekFromJSON(json: any): PlanWeek { + return PlanWeekFromJSONTyped(json, false); +} + +export function PlanWeekFromJSONTyped(json: any, ignoreDiscriminator: boolean): PlanWeek { + if ((json === undefined) || (json === null)) { + return json; + } + return { + + 'date': (new Date(json['date'])), + 'items': !exists(json, 'items') ? undefined : ((json['items'] as Array).map(PlanWeekItemFromJSON)), + }; +} + +export function PlanWeekToJSON(value?: PlanWeek | null): any { + if (value === undefined) { + return undefined; + } + if (value === null) { + return null; + } + return { + + 'date': (value.date.toISOString().substr(0,10)), + 'items': value.items === undefined ? undefined : ((value.items as Array).map(PlanWeekItemToJSON)), + }; +} + diff --git a/frontend/src/dashclient/models/PlanWeekItem.ts b/frontend/src/dashclient/models/PlanWeekItem.ts new file mode 100644 index 0000000..09241a3 --- /dev/null +++ b/frontend/src/dashclient/models/PlanWeekItem.ts @@ -0,0 +1,82 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Dash API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { exists, mapValues } from '../runtime'; +/** + * + * @export + * @interface PlanWeekItem + */ +export interface PlanWeekItem { + /** + * + * @type {string} + * @memberof PlanWeekItem + */ + item: string; + /** + * + * @type {number} + * @memberof PlanWeekItem + */ + numTodos?: number; + /** + * + * @type {number} + * @memberof PlanWeekItem + */ + numDone?: number; +} + +/** + * Check if a given object implements the PlanWeekItem interface. + */ +export function instanceOfPlanWeekItem(value: object): boolean { + let isInstance = true; + isInstance = isInstance && "item" in value; + + return isInstance; +} + +export function PlanWeekItemFromJSON(json: any): PlanWeekItem { + return PlanWeekItemFromJSONTyped(json, false); +} + +export function PlanWeekItemFromJSONTyped(json: any, ignoreDiscriminator: boolean): PlanWeekItem { + if ((json === undefined) || (json === null)) { + return json; + } + return { + + 'item': json['item'], + 'numTodos': !exists(json, 'numTodos') ? undefined : json['numTodos'], + 'numDone': !exists(json, 'numDone') ? undefined : json['numDone'], + }; +} + +export function PlanWeekItemToJSON(value?: PlanWeekItem | null): any { + if (value === undefined) { + return undefined; + } + if (value === null) { + return null; + } + return { + + 'item': value.item, + 'numTodos': value.numTodos, + 'numDone': value.numDone, + }; +} + diff --git a/frontend/src/dashclient/models/index.ts b/frontend/src/dashclient/models/index.ts index 86be49e..e460347 100644 --- a/frontend/src/dashclient/models/index.ts +++ b/frontend/src/dashclient/models/index.ts @@ -1,3 +1,7 @@ /* tslint:disable */ /* eslint-disable */ export * from './JournalEntry'; +export * from './PlanDay'; +export * from './PlanMonth'; +export * from './PlanWeek'; +export * from './PlanWeekItem'; diff --git a/frontend/src/stores/apiStore.ts b/frontend/src/stores/apiStore.ts index a720dfc..49df157 100644 --- a/frontend/src/stores/apiStore.ts +++ b/frontend/src/stores/apiStore.ts @@ -1,4 +1,4 @@ -import { Configuration, DefaultApi } from "../dashclient"; +import { Configuration, JournalApi, PlanApi } from "../dashclient"; console.log(window.location.origin) @@ -6,4 +6,5 @@ const configuration = new Configuration({ basePath: "http://192.168.0.181:8080/api/v1", }); -export const dashApi = new DefaultApi(configuration); \ No newline at end of file +export const journalApi = new JournalApi(configuration); +export const planApi = new PlanApi(configuration); \ No newline at end of file diff --git a/frontend/src/stores/appStore.ts b/frontend/src/stores/appStore.ts index 057e113..357464e 100644 --- a/frontend/src/stores/appStore.ts +++ b/frontend/src/stores/appStore.ts @@ -1,6 +1,7 @@ import { writable, readable } from 'svelte/store'; -export const curTab = writable("Journal"); +// export const curTab = writable("Journal"); +export const curTab = writable("Plan"); export const currentDate = writable(new Date()); diff --git a/frontend/src/stores/planStore.ts b/frontend/src/stores/planStore.ts new file mode 100644 index 0000000..65d1964 --- /dev/null +++ b/frontend/src/stores/planStore.ts @@ -0,0 +1,4 @@ +export interface PlanItem { + id: number, + text: string +} \ No newline at end of file