From 644482a48507ed7564698590cf19196d24ece0b3 Mon Sep 17 00:00:00 2001 From: rapoler Date: Thu, 1 Aug 2024 11:18:05 +0530 Subject: [PATCH 1/3] added filtering by year and month to media --- server/service/core/action/medium/list.go | 25 ++++++++++++++++- server/service/core/service/medium.go | 33 ++++++++++++++++++----- studio/src/actions/media.js | 8 +++--- studio/src/pages/media/index.js | 23 +++++++++++++--- studio/src/utils/filters.js | 6 ++++- studio/src/utils/getUrlParams.js | 5 ++-- 6 files changed, 83 insertions(+), 17 deletions(-) diff --git a/server/service/core/action/medium/list.go b/server/service/core/action/medium/list.go index 44affd7e9..ab1ceb608 100644 --- a/server/service/core/action/medium/list.go +++ b/server/service/core/action/medium/list.go @@ -1,7 +1,9 @@ package medium import ( + "fmt" "net/http" + "time" "github.com/factly/dega-server/service/core/service" "github.com/factly/dega-server/util" @@ -37,9 +39,30 @@ func list(w http.ResponseWriter, r *http.Request) { searchQuery := r.URL.Query().Get("q") sort := r.URL.Query().Get("sort") offset, limit := paginationx.Parse(r.URL.Query()) + dateStr := r.URL.Query().Get("year_month") + + var year int + var month time.Month + var yearMonthProvided bool + + // Only parse date if dateStr is present + if dateStr != "" { + layout := "2006-01" + date, err := time.Parse(layout, dateStr) + if err != nil { + fmt.Println("Error parsing date:", err) + return + } + //fmt.Println(date, dateStr, "--------------------------------------------------------") + year = date.Year() + month = date.Month() + //fmt.Printf("Year: %d, Month: %d\n", year, month) + + yearMonthProvided = true + } mediumService := service.GetMediumService() - result, errMessages := mediumService.List(authCtx.SpaceID, offset, limit, searchQuery, sort) + result, errMessages := mediumService.List(authCtx.SpaceID, offset, limit, searchQuery, sort, year, month, yearMonthProvided) if errMessages != nil { errorx.Render(w, errMessages) return diff --git a/server/service/core/service/medium.go b/server/service/core/service/medium.go index 407bf358b..0ad18b0a7 100644 --- a/server/service/core/service/medium.go +++ b/server/service/core/service/medium.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "net/http" "strings" "time" @@ -40,6 +41,7 @@ type Medium struct { type pagingMedium struct { Total int64 `json:"total"` Nodes []model.Medium `json:"nodes"` + Date time.Time `json:"date"` } type MediumService struct { @@ -47,7 +49,7 @@ type MediumService struct { } type IMediumService interface { GetById(sID, id uuid.UUID) (model.Medium, error) - List(sID uuid.UUID, offset, limit int, searchQuery, sort string) (pagingMedium, []errorx.Message) + List(sID uuid.UUID, offset, limit int, searchQuery, sort string, year int, month time.Month, yearMonthProvided bool) (pagingMedium, []errorx.Message) Create(ctx context.Context, sID uuid.UUID, uID string, mediumList []Medium) (pagingMedium, []errorx.Message) Update(sID, id uuid.UUID, uID string, medium *Medium) (model.Medium, []errorx.Message) Delete(sID, id uuid.UUID) []errorx.Message @@ -71,17 +73,36 @@ func (ms MediumService) GetById(sID, id uuid.UUID) (model.Medium, error) { return *result, err } -func (ms MediumService) List(sID uuid.UUID, offset, limit int, searchQuery, sort string) (pagingMedium, []errorx.Message) { +func (ms MediumService) List(sID uuid.UUID, offset, limit int, searchQuery, sort string, year int, month time.Month, yearMonthProvided bool) (pagingMedium, []errorx.Message) { result := pagingMedium{} result.Nodes = make([]model.Medium, 0) + tx := config.DB.Model(&model.Medium{}).Where(&model.Medium{ + SpaceID: sID, + }) + + if yearMonthProvided { + // Validate year and month inputs + if year < 1 || (month < 1 || month > 12) { + return pagingMedium{}, []errorx.Message{errorx.GetMessage("Invalid year or month", http.StatusBadRequest)} + } + + // Add year filter + if year > 0 { + tx = tx.Where("EXTRACT(YEAR FROM created_at) = ?", year) + } + + // Add month filter + if month > 0 && month <= 12 { + tx = tx.Where("EXTRACT(MONTH FROM created_at) = ?", int(month)) + } + + } + if sort != "asc" { sort = "desc" } - - tx := config.DB.Model(&model.Medium{}).Where(&model.Medium{ - SpaceID: sID, - }).Order("created_at " + sort) + tx = tx.Order("created_at " + sort) if searchQuery != "" { diff --git a/studio/src/actions/media.js b/studio/src/actions/media.js index 881bb68dd..5211cb2a6 100644 --- a/studio/src/actions/media.js +++ b/studio/src/actions/media.js @@ -20,12 +20,13 @@ export const getMedia = (query, profile) => { params: query, }) .then((response) => { - dispatch(addMedia(response.data.nodes)); + if (response.data === "") return []; + dispatch(addMedia(response?.data?.nodes)); dispatch( addMediaRequest({ - data: response.data.nodes.map((item) => item.id), + data: response?.data?.nodes.map((item) => item.id), query: query, - total: response.data.total, + total: response?.data?.total, }), ); }) @@ -59,7 +60,6 @@ export const createMedium = (data, profile) => { return axios .post(MEDIA_API, profile ? data[0] : data) .then((response) => { - dispatch(resetMedia()); dispatch(addSuccessNotification('Medium created')); return profile ? response.data : response.data.nodes[0]; }) diff --git a/studio/src/pages/media/index.js b/studio/src/pages/media/index.js index f0f896dd9..6e4623082 100644 --- a/studio/src/pages/media/index.js +++ b/studio/src/pages/media/index.js @@ -11,8 +11,9 @@ import { ConfigProvider, Typography, Tooltip, + DatePicker } from 'antd'; -import { PlusOutlined, SearchOutlined } from '@ant-design/icons'; +import { PlusOutlined,SearchOutlined } from '@ant-design/icons'; import { useDispatch, useSelector } from 'react-redux'; import MediumList from './components/MediumList'; import { getMedia } from '../../actions/media'; @@ -33,7 +34,6 @@ function Media({ permission }) { ...params, }); const [searchFieldExpand, setSearchFieldExpand] = React.useState(false); - const pathName = useLocation().pathname; useEffect(() => { @@ -50,7 +50,6 @@ function Media({ permission }) { const node = state.media.req.find((item) => { return deepEqual(item.query, params); }); - if (node) return { media: node.data.map((element) => state.media.details[element]), @@ -59,9 +58,11 @@ function Media({ permission }) { }; return { media: [], total: 0, loading: state.media.loading }; }); + useEffect(() => { if (form) form.setFieldsValue(new Filters(params)); }, [params]); + React.useEffect(() => { fetchMedia(); // eslint-disable-next-line react-hooks/exhaustive-deps @@ -71,6 +72,15 @@ function Media({ permission }) { dispatch(getMedia(filters)); }; + const onDateChange = (date, dateString) => { + //console.log(date, dateString); + const updatedFilters = { + ...filters, + year_month: dateString, + }; + setFilters(updatedFilters); + } + return loading ? ( ) : ( @@ -178,6 +188,13 @@ function Media({ permission }) { + + + + + + + diff --git a/studio/src/utils/filters.js b/studio/src/utils/filters.js index c9906e8e0..bc7530a8e 100644 --- a/studio/src/utils/filters.js +++ b/studio/src/utils/filters.js @@ -1,6 +1,6 @@ class Filters { constructor( - { q, sort, tag, category, author, status, claimant, rating, podcast, language } = { + { q, sort, tag, category, author, status, claimant, rating, podcast, language, year, month } = { q: null, sort: 'asc', tag: [], @@ -11,6 +11,8 @@ class Filters { status: 'all', podcast: [], language: 'all', + year: null, + month: null, }, ) { this.q = q; @@ -23,6 +25,8 @@ class Filters { this.podcast = podcast; this.language = language; this.rating = rating; + this.year = year; + this.month = month; } } diff --git a/studio/src/utils/getUrlParams.js b/studio/src/utils/getUrlParams.js index 3443ae4b7..c355dd666 100644 --- a/studio/src/utils/getUrlParams.js +++ b/studio/src/utils/getUrlParams.js @@ -1,9 +1,10 @@ function getUrlParams(query, filters) { - const keys = filters ? filters : ['page', 'limit', 'q', 'sort']; + const keys = filters ? filters : ['page', 'limit', 'q', 'sort','year_month']; const params = { sort: 'desc', limit: 10, page: 1, + year_month: '', }; keys.forEach((key) => { if (query.get(key)) { @@ -18,7 +19,7 @@ function getUrlParams(query, filters) { ) { const val = query.getAll(key).map((v) => parseInt(v)); params[key] = val; - } else if (key === 'sort' || key === 'q' || key === 'status' || key === 'language') { + } else if (key === 'sort' || key === 'q' || key === 'status' || key === 'language' || key === 'year_month') { params[key] = query.get(key); } else { params[key] = parseInt(query.get(key)); From eb91db93b598c6e98f4624c1ecf389dd21c525ad Mon Sep 17 00:00:00 2001 From: rapoler Date: Fri, 2 Aug 2024 11:39:25 +0530 Subject: [PATCH 2/3] added filtering by year and month to media, made requiured changes --- package-lock.json | 156 ++++++++++++++++++ package.json | 2 + server/service/core/action/medium/list.go | 14 +- server/service/core/service/medium.go | 4 +- .../src/pages/media/components/MediumList.js | 6 +- studio/src/pages/media/index.js | 42 +++-- studio/src/utils/getUrlParams.js | 5 +- 7 files changed, 199 insertions(+), 30 deletions(-) create mode 100644 package-lock.json diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 000000000..eaf2d0bba --- /dev/null +++ b/package-lock.json @@ -0,0 +1,156 @@ +{ + "name": "dega", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "dega", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "moment": "^2.30.1" + }, + "devDependencies": { + "@playwright/test": "^1.44.1", + "@types/node": "^20.14.2" + } + }, + "node_modules/@playwright/test": { + "version": "1.44.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.44.1.tgz", + "integrity": "sha512-1hZ4TNvD5z9VuhNJ/walIjvMVvYkZKf71axoF/uiAqpntQJXpG64dlXhoDXE3OczPuTuvjf/M5KWFg5VAVUS3Q==", + "dev": true, + "dependencies": { + "playwright": "1.44.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@types/node": { + "version": "20.14.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.2.tgz", + "integrity": "sha512-xyu6WAMVwv6AKFLB+e/7ySZVr/0zLCzOa7rSpq6jNwpqOrUbcACDWC+53d4n2QHOnDou0fbIsg8wZu/sxrnI4Q==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "engines": { + "node": "*" + } + }, + "node_modules/playwright": { + "version": "1.44.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.44.1.tgz", + "integrity": "sha512-qr/0UJ5CFAtloI3avF95Y0L1xQo6r3LQArLIg/z/PoGJ6xa+EwzrwO5lpNr/09STxdHuUoP2mvuELJS+hLdtgg==", + "dev": true, + "dependencies": { + "playwright-core": "1.44.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=16" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.44.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.44.1.tgz", + "integrity": "sha512-wh0JWtYTrhv1+OSsLPgFzGzt67Y7BE/ZS3jEqgGBlp2ppp1ZDj8c+9IARNW4dwf1poq5MgHreEM2KV/GuR4cFA==", + "dev": true, + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + } + }, + "dependencies": { + "@playwright/test": { + "version": "1.44.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.44.1.tgz", + "integrity": "sha512-1hZ4TNvD5z9VuhNJ/walIjvMVvYkZKf71axoF/uiAqpntQJXpG64dlXhoDXE3OczPuTuvjf/M5KWFg5VAVUS3Q==", + "dev": true, + "requires": { + "playwright": "1.44.1" + } + }, + "@types/node": { + "version": "20.14.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.2.tgz", + "integrity": "sha512-xyu6WAMVwv6AKFLB+e/7ySZVr/0zLCzOa7rSpq6jNwpqOrUbcACDWC+53d4n2QHOnDou0fbIsg8wZu/sxrnI4Q==", + "dev": true, + "requires": { + "undici-types": "~5.26.4" + } + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, + "moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==" + }, + "playwright": { + "version": "1.44.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.44.1.tgz", + "integrity": "sha512-qr/0UJ5CFAtloI3avF95Y0L1xQo6r3LQArLIg/z/PoGJ6xa+EwzrwO5lpNr/09STxdHuUoP2mvuELJS+hLdtgg==", + "dev": true, + "requires": { + "fsevents": "2.3.2", + "playwright-core": "1.44.1" + } + }, + "playwright-core": { + "version": "1.44.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.44.1.tgz", + "integrity": "sha512-wh0JWtYTrhv1+OSsLPgFzGzt67Y7BE/ZS3jEqgGBlp2ppp1ZDj8c+9IARNW4dwf1poq5MgHreEM2KV/GuR4cFA==", + "dev": true + }, + "undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + } + } +} diff --git a/package.json b/package.json index aa982a619..30fd1117f 100644 --- a/package.json +++ b/package.json @@ -18,5 +18,7 @@ "devDependencies": { "@playwright/test": "^1.44.1", "@types/node": "^20.14.2" + }, + "dependencies": { } } diff --git a/server/service/core/action/medium/list.go b/server/service/core/action/medium/list.go index ab1ceb608..7442ffd92 100644 --- a/server/service/core/action/medium/list.go +++ b/server/service/core/action/medium/list.go @@ -39,30 +39,26 @@ func list(w http.ResponseWriter, r *http.Request) { searchQuery := r.URL.Query().Get("q") sort := r.URL.Query().Get("sort") offset, limit := paginationx.Parse(r.URL.Query()) - dateStr := r.URL.Query().Get("year_month") + dateStr := r.URL.Query().Get("date") var year int var month time.Month var yearMonthProvided bool - // Only parse date if dateStr is present if dateStr != "" { layout := "2006-01" - date, err := time.Parse(layout, dateStr) + dateInt, err := time.Parse(layout, dateStr) if err != nil { fmt.Println("Error parsing date:", err) return } - //fmt.Println(date, dateStr, "--------------------------------------------------------") - year = date.Year() - month = date.Month() - //fmt.Printf("Year: %d, Month: %d\n", year, month) - + year = dateInt.Year() + month = dateInt.Month() yearMonthProvided = true } mediumService := service.GetMediumService() - result, errMessages := mediumService.List(authCtx.SpaceID, offset, limit, searchQuery, sort, year, month, yearMonthProvided) + result, errMessages := mediumService.List(authCtx.SpaceID, offset, limit, searchQuery, sort, year, month, yearMonthProvided, dateStr) if errMessages != nil { errorx.Render(w, errMessages) return diff --git a/server/service/core/service/medium.go b/server/service/core/service/medium.go index 0ad18b0a7..0ce74047e 100644 --- a/server/service/core/service/medium.go +++ b/server/service/core/service/medium.go @@ -49,7 +49,7 @@ type MediumService struct { } type IMediumService interface { GetById(sID, id uuid.UUID) (model.Medium, error) - List(sID uuid.UUID, offset, limit int, searchQuery, sort string, year int, month time.Month, yearMonthProvided bool) (pagingMedium, []errorx.Message) + List(sID uuid.UUID, offset, limit int, searchQuery, sort string, year int, month time.Month, yearMonthProvided bool, dateStr string) (pagingMedium, []errorx.Message) Create(ctx context.Context, sID uuid.UUID, uID string, mediumList []Medium) (pagingMedium, []errorx.Message) Update(sID, id uuid.UUID, uID string, medium *Medium) (model.Medium, []errorx.Message) Delete(sID, id uuid.UUID) []errorx.Message @@ -73,7 +73,7 @@ func (ms MediumService) GetById(sID, id uuid.UUID) (model.Medium, error) { return *result, err } -func (ms MediumService) List(sID uuid.UUID, offset, limit int, searchQuery, sort string, year int, month time.Month, yearMonthProvided bool) (pagingMedium, []errorx.Message) { +func (ms MediumService) List(sID uuid.UUID, offset, limit int, searchQuery, sort string, year int, month time.Month, yearMonthProvided bool, dateStr string) (pagingMedium, []errorx.Message) { result := pagingMedium{} result.Nodes = make([]model.Medium, 0) diff --git a/studio/src/pages/media/components/MediumList.js b/studio/src/pages/media/components/MediumList.js index 1937967cf..a7593b61a 100644 --- a/studio/src/pages/media/components/MediumList.js +++ b/studio/src/pages/media/components/MediumList.js @@ -15,7 +15,7 @@ function MediumList({ data, filters, setFilters }) { xl: 4, xxl: 5, }} - pagination={{ + pagination={data.total > 0 ? { showTotal: (total, range) => `${range[0]}-${range[1]} of ${total} results`, total: data.total, current: filters.page, @@ -24,7 +24,7 @@ function MediumList({ data, filters, setFilters }) { setFilters({ ...filters, page: pageNumber || 1, limit: pageSize }); }, pageSizeOptions: ['10', '15', '20'], - }} + } : false} dataSource={data.media} renderItem={(item) => ( @@ -32,7 +32,6 @@ function MediumList({ data, filters, setFilters }) { { history({ pathname: pathName, @@ -60,8 +66,11 @@ function Media({ permission }) { }); useEffect(() => { - if (form) form.setFieldsValue(new Filters(params)); - }, [params]); + if (form) { + const initialFilters = new Filters(params); + form.setFieldsValue(initialFilters); + } + }, [params, form]); React.useEffect(() => { fetchMedia(); @@ -73,13 +82,12 @@ function Media({ permission }) { }; const onDateChange = (date, dateString) => { - //console.log(date, dateString); const updatedFilters = { ...filters, - year_month: dateString, + date: dateString, }; setFilters(updatedFilters); - } + }; return loading ? ( @@ -95,8 +103,9 @@ function Media({ permission }) { }, }} > +
{ @@ -121,8 +130,11 @@ function Media({ permission }) { } setFilters({ ...filters, ...changedValues }); } + + console.log(allValues); }} > + @@ -190,9 +202,12 @@ function Media({ permission }) { - - - + + + @@ -208,6 +223,7 @@ function Media({ permission }) { filters={filters} setFilters={setFilters} /> + ); } diff --git a/studio/src/utils/getUrlParams.js b/studio/src/utils/getUrlParams.js index c355dd666..4f900c171 100644 --- a/studio/src/utils/getUrlParams.js +++ b/studio/src/utils/getUrlParams.js @@ -1,10 +1,9 @@ function getUrlParams(query, filters) { - const keys = filters ? filters : ['page', 'limit', 'q', 'sort','year_month']; + const keys = filters ? filters : ['page', 'limit', 'q', 'sort','date']; const params = { sort: 'desc', limit: 10, page: 1, - year_month: '', }; keys.forEach((key) => { if (query.get(key)) { @@ -19,7 +18,7 @@ function getUrlParams(query, filters) { ) { const val = query.getAll(key).map((v) => parseInt(v)); params[key] = val; - } else if (key === 'sort' || key === 'q' || key === 'status' || key === 'language' || key === 'year_month') { + } else if (key === 'sort' || key === 'q' || key === 'status' || key === 'language' || key === 'date') { params[key] = query.get(key); } else { params[key] = parseInt(query.get(key)); From 41e383344983249474a4bb39b3f8f0251fbc3715 Mon Sep 17 00:00:00 2001 From: rapoler Date: Fri, 2 Aug 2024 13:46:47 +0530 Subject: [PATCH 3/3] removed incomplete date part --- studio/src/pages/media/index.js | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/studio/src/pages/media/index.js b/studio/src/pages/media/index.js index ae891e402..07f9ee2af 100644 --- a/studio/src/pages/media/index.js +++ b/studio/src/pages/media/index.js @@ -1,5 +1,4 @@ -import React, {useState ,useEffect } from 'react'; -import dayjs from 'dayjs'; +import React, { useState, useEffect } from 'react'; import { Space, Button, @@ -37,11 +36,8 @@ function Media({ permission }) { const [searchFieldExpand, setSearchFieldExpand] = React.useState(false); const pathName = useLocation().pathname; - - const monthFormat = 'YYYY-MM'; const [date, setDate] = useState(new Date()); - useEffect(() => { history({ pathname: pathName, @@ -103,7 +99,6 @@ function Media({ permission }) { }, }} > - - @@ -203,10 +197,7 @@ function Media({ permission }) { - + @@ -223,7 +214,6 @@ function Media({ permission }) { filters={filters} setFilters={setFilters} /> - ); }