diff --git a/src/App.tsx b/src/App.tsx index 3acd3833e9..b50dd048d4 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -17,59 +17,59 @@ import { useAppDispatch } from "./store"; import { fetchOcVersion, fetchUserInfo } from "./slices/userInfoSlice"; function App() { - const dispatch = useAppDispatch(); - useEffect(() => { - // Load information about current user on mount - dispatch(fetchUserInfo()); - // Load information about current opencast version on mount - dispatch(fetchOcVersion()); + const dispatch = useAppDispatch(); + useEffect(() => { + // Load information about current user on mount + dispatch(fetchUserInfo()); + // Load information about current opencast version on mount + dispatch(fetchOcVersion()); - // Add event listener for back button to check if we are still logged in - window.addEventListener("popstate", function(event) { - dispatch(fetchUserInfo()); - }); + // Add event listener for back button to check if we are still logged in + window.addEventListener("popstate", function(event) { + dispatch(fetchUserInfo()); + }); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); - return ( - - - } /> + return ( + + + } /> - } /> + } /> - } /> + } /> - } /> + } /> - } /> + } /> - } /> + } /> - } /> + } /> - } /> + } /> - } /> + } /> - } /> + } /> - } /> + } /> - } /> + } /> - } /> + } /> - } /> + } /> - } - /> - - - ); + } + /> + + + ); } export default App; diff --git a/src/configs/adopterRegistrationConfig.ts b/src/configs/adopterRegistrationConfig.ts index 486e8edc99..efc31262df 100644 --- a/src/configs/adopterRegistrationConfig.ts +++ b/src/configs/adopterRegistrationConfig.ts @@ -1,1170 +1,1170 @@ // states contains the different states and their configurations of the adapter registration modal export const states = { - information: { - nextState: { - 0: "close", - 1: "form", - 2: "skip", - }, - buttons: { - submit: true, - back: false, - skip: true, - close: true, - submitButtonText: "ADOPTER_REGISTRATION.MODAL.CONTINUE", - }, - }, - form: { - nextState: { - 0: "close", - 1: "summary", - 2: "legal_info", - 3: "update", - 4: "delete_submit", - 5: "information", - }, - buttons: { - submit: true, - back: true, - skip: false, - close: true, - delete: false, - submitButtonText: "ADOPTER_REGISTRATION.MODAL.CONTINUE", - }, - }, - save: { - nextState: { - 0: "thank_you", - 1: "error", - }, - buttons: { - submit: false, - back: false, - skip: false, - close: false, - submitButtonText: "", - }, - }, - update: { - nextState: { - 0: "close", - 1: "error", - }, - buttons: { - submit: false, - back: false, - skip: false, - close: false, - submitButtonText: "", - }, - }, - delete_submit: { - nextState: { - 0: "thank_you", - 1: "error", - 5: "form", - }, - buttons: { - submit: true, - back: true, - skip: false, - close: true, - submitButtonText: "ADOPTER_REGISTRATION.MODAL.CONFIRM", - }, - }, - delete: { - nextState: { - 0: "close", - 1: "error", - }, - buttons: { - submit: false, - back: false, - skip: false, - close: false, - submitButtonText: "", - }, - }, - summary: { - nextState: { - 0: "thank_you", - 1: "error", - 5: "form", - }, - buttons: { - submit: true, - back: true, - skip: false, - close: true, - submitButtonText: "ADOPTER_REGISTRATION.MODAL.SUBMIT", - }, - }, - thank_you: { - nextState: { - 0: "close", - 1: "error", - }, - buttons: { - submit: false, - back: false, - skip: false, - close: true, - submitButtonText: "", - }, - }, - error: { - nextState: { - 0: "close", - 1: "error", - }, - buttons: { - submit: false, - back: false, - skip: false, - close: true, - submitButtonText: "", - }, - }, - skip: { - nextState: { - 0: "close", - 1: "error", - }, - buttons: { - submit: false, - back: false, - skip: false, - close: true, - submitButtonText: "", - }, - }, - legal_info: { - nextState: { - 0: "close", - 1: "error", - 5: "form", - }, - buttons: { - submit: false, - back: true, - skip: false, - close: true, - submitButtonText: "", - }, - }, + information: { + nextState: { + 0: "close", + 1: "form", + 2: "skip", + }, + buttons: { + submit: true, + back: false, + skip: true, + close: true, + submitButtonText: "ADOPTER_REGISTRATION.MODAL.CONTINUE", + }, + }, + form: { + nextState: { + 0: "close", + 1: "summary", + 2: "legal_info", + 3: "update", + 4: "delete_submit", + 5: "information", + }, + buttons: { + submit: true, + back: true, + skip: false, + close: true, + delete: false, + submitButtonText: "ADOPTER_REGISTRATION.MODAL.CONTINUE", + }, + }, + save: { + nextState: { + 0: "thank_you", + 1: "error", + }, + buttons: { + submit: false, + back: false, + skip: false, + close: false, + submitButtonText: "", + }, + }, + update: { + nextState: { + 0: "close", + 1: "error", + }, + buttons: { + submit: false, + back: false, + skip: false, + close: false, + submitButtonText: "", + }, + }, + delete_submit: { + nextState: { + 0: "thank_you", + 1: "error", + 5: "form", + }, + buttons: { + submit: true, + back: true, + skip: false, + close: true, + submitButtonText: "ADOPTER_REGISTRATION.MODAL.CONFIRM", + }, + }, + delete: { + nextState: { + 0: "close", + 1: "error", + }, + buttons: { + submit: false, + back: false, + skip: false, + close: false, + submitButtonText: "", + }, + }, + summary: { + nextState: { + 0: "thank_you", + 1: "error", + 5: "form", + }, + buttons: { + submit: true, + back: true, + skip: false, + close: true, + submitButtonText: "ADOPTER_REGISTRATION.MODAL.SUBMIT", + }, + }, + thank_you: { + nextState: { + 0: "close", + 1: "error", + }, + buttons: { + submit: false, + back: false, + skip: false, + close: true, + submitButtonText: "", + }, + }, + error: { + nextState: { + 0: "close", + 1: "error", + }, + buttons: { + submit: false, + back: false, + skip: false, + close: true, + submitButtonText: "", + }, + }, + skip: { + nextState: { + 0: "close", + 1: "error", + }, + buttons: { + submit: false, + back: false, + skip: false, + close: true, + submitButtonText: "", + }, + }, + legal_info: { + nextState: { + 0: "close", + 1: "error", + 5: "form", + }, + buttons: { + submit: false, + back: true, + skip: false, + close: true, + submitButtonText: "", + }, + }, }; export const systemTypes = [ - { - value: "production", - name: "ADOPTER_REGISTRATION.MODAL.FORM_STATE.SYSTEM_TYPE_PRODUCTION", - }, - { - value: "test", - name: "ADOPTER_REGISTRATION.MODAL.FORM_STATE.SYSTEM_TYPE_TEST", - }, + { + value: "production", + name: "ADOPTER_REGISTRATION.MODAL.FORM_STATE.SYSTEM_TYPE_PRODUCTION", + }, + { + value: "test", + name: "ADOPTER_REGISTRATION.MODAL.FORM_STATE.SYSTEM_TYPE_TEST", + }, ]; // countries that an adopter can choose as country of origin export const countries = [ - { - code: "AF", - name: "Afghanistan", - }, - { - code: "AL", - name: "Albania", - }, - { - code: "DE", - name: "Germany", - }, - { - code: "AD", - name: "Andorra", - }, - { - code: "AO", - name: "Angola", - }, - { - code: "AI", - name: "Anguilla", - }, - { - code: "AG", - name: "Antigua and Barbuda", - }, - { - code: "AQ", - name: "Antarctica", - }, - { - code: "SA", - name: "Saudi Arabia", - }, - { - code: "DZ", - name: "Algeria", - }, - { - code: "AR", - name: "Argentina", - }, - { - code: "AM", - name: "Armenia", - }, - { - code: "AW", - name: "Aruba", - }, - { - code: "AU", - name: "Australia", - }, - { - code: "AT", - name: "Austria", - }, - { - code: "AZ", - name: "Azerbaijan", - }, - { - code: "BS", - name: "Bahamas", - }, - { - code: "BH", - name: "Bahrain", - }, - { - code: "BD", - name: "Bangladesh", - }, - { - code: "BB", - name: "Barbados", - }, - { - code: "BZ", - name: "Belize", - }, - { - code: "BJ", - name: "Benin", - }, - { - code: "BM", - name: "Bermuda", - }, - { - code: "BY", - name: "Belarus", - }, - { - code: "BO", - name: "Bolivia", - }, - { - code: "BQ", - name: "Bonaire", - }, - { - code: "BA", - name: "Bosnia and Herzegovina", - }, - { - code: "BW", - name: "Botswana", - }, - { - code: "BR", - name: "Brazil", - }, - { - code: "BN", - name: "Brunei", - }, - { - code: "BG", - name: "Bulgaria", - }, - { - code: "BF", - name: "Burkina Faso", - }, - { - code: "BI", - name: "Burundi", - }, - { - code: "BT", - name: "Bhutan", - }, - { - code: "BE", - name: "Belgium", - }, - { - code: "CV", - name: "Cabo Verde", - }, - { - code: "KH", - name: "Cambodia", - }, - { - code: "CM", - name: "Cameroon", - }, - { - code: "CA", - name: "Canada", - }, - { - code: "TD", - name: "Chad", - }, - { - code: "CL", - name: "Chile", - }, - { - code: "CN", - name: "China", - }, - { - code: "CY", - name: "Cyprus", - }, - { - code: "VA", - name: "Vatican City", - }, - { - code: "CO", - name: "Colombia", - }, - { - code: "KM", - name: "Comoros", - }, - { - code: "CG", - name: "Republic of the Congo", - }, - { - code: "KP", - name: "North Korea", - }, - { - code: "KR", - name: "South Korea", - }, - { - code: "CR", - name: "Costa Rica", - }, - { - code: "CI", - name: "Ivory Coast", - }, - { - code: "HR", - name: "Croatia", - }, - { - code: "CU", - name: "Cuba", - }, - { - code: "CW", - name: "Curacao", - }, - { - code: "DK", - name: "Denmark", - }, - { - code: "DM", - name: "Dominica", - }, - { - code: "EC", - name: "Ecuador", - }, - { - code: "EG", - name: "Egypt", - }, - { - code: "SV", - name: "El Salvador", - }, - { - code: "AE", - name: "United Arab Emirates", - }, - { - code: "ER", - name: "Eritrea", - }, - { - code: "SK", - name: "Slovakia", - }, - { - code: "SI", - name: "Slovenia", - }, - { - code: "ES", - name: "Spain", - }, - { - code: "US", - name: "United States", - }, - { - code: "EE", - name: "Estonia", - }, - { - code: "ET", - name: "Ethiopia", - }, - { - code: "PH", - name: "Philippines", - }, - { - code: "FI", - name: "Finland", - }, - { - code: "FJ", - name: "Fiji", - }, - { - code: "FR", - name: "France", - }, - { - code: "GA", - name: "Gabon", - }, - { - code: "GM", - name: "Gambia", - }, - { - code: "GE", - name: "Georgia", - }, - { - code: "GH", - name: "Ghana", - }, - { - code: "GI", - name: "Gibraltar", - }, - { - code: "GD", - name: "Grenada", - }, - { - code: "GR", - name: "Greece", - }, - { - code: "GL", - name: "Greenland", - }, - { - code: "GP", - name: "Guadeloupe", - }, - { - code: "GU", - name: "Guam", - }, - { - code: "GT", - name: "Guatemala", - }, - { - code: "GF", - name: "French Guiana", - }, - { - code: "GG", - name: "Guernsey", - }, - { - code: "GN", - name: "Guinea", - }, - { - code: "GQ", - name: "Equatorial Guinea", - }, - { - code: "GW", - name: "Guinea-Bissau", - }, - { - code: "GY", - name: "Guyana", - }, - { - code: "HT", - name: "Haiti", - }, - { - code: "NL", - name: "Netherlands", - }, - { - code: "HN", - name: "Honduras", - }, - { - code: "HK", - name: "Hong Kong", - }, - { - code: "HU", - name: "Hungary", - }, - { - code: "IN", - name: "India", - }, - { - code: "ID", - name: "Indonesia", - }, - { - code: "IQ", - name: "Iraq", - }, - { - code: "IE", - name: "Ireland", - }, - { - code: "IR", - name: "Iran", - }, - { - code: "BV", - name: "Bouvet Island", - }, - { - code: "CX", - name: "Christmas Island", - }, - { - code: "NU", - name: "Niue", - }, - { - code: "NF", - name: "Norfolk Island", - }, - { - code: "IM", - name: "Isle of Man", - }, - { - code: "IS", - name: "Iceland", - }, - { - code: "KY", - name: "Cayman Islands", - }, - { - code: "CC", - name: "Cocos [Keeling] Islands", - }, - { - code: "CK", - name: "Cook Islands", - }, - { - code: "FO", - name: "Faroe Islands", - }, - { - code: "GS", - name: "South Georgia and the South Sandwich Islands", - }, - { - code: "HM", - name: "Heard Island and McDonald Islands", - }, - { - code: "FK", - name: "Falkland Islands", - }, - { - code: "MP", - name: "Northern Mariana Islands", - }, - { - code: "MH", - name: "Marshall Islands", - }, - { - code: "PN", - name: "Pitcairn Islands", - }, - { - code: "SB", - name: "Solomon Islands", - }, - { - code: "TC", - name: "Turks and Caicos Islands", - }, - { - code: "VG", - name: "British Virgin Islands", - }, - { - code: "VI", - name: "U.S. Virgin Islands", - }, - { - code: "AX", - name: "Åland", - }, - { - code: "UM", - name: "U.S. Minor Outlying Islands", - }, - { - code: "IL", - name: "Israel", - }, - { - code: "IT", - name: "Italy", - }, - { - code: "JM", - name: "Jamaica", - }, - { - code: "JP", - name: "Japan", - }, - { - code: "JE", - name: "Jersey", - }, - { - code: "JO", - name: "Jordan", - }, - { - code: "KZ", - name: "Kazakhstan", - }, - { - code: "KE", - name: "Kenya", - }, - { - code: "KG", - name: "Kyrgyzstan", - }, - { - code: "KI", - name: "Kiribati", - }, - { - code: "XK", - name: "Kosovo", - }, - { - code: "KW", - name: "Kuwait", - }, - { - code: "LA", - name: "Laos", - }, - { - code: "LS", - name: "Lesotho", - }, - { - code: "LV", - name: "Latvia", - }, - { - code: "LR", - name: "Liberia", - }, - { - code: "LY", - name: "Libya", - }, - { - code: "LI", - name: "Liechtenstein", - }, - { - code: "LT", - name: "Lithuania", - }, - { - code: "LU", - name: "Luxembourg", - }, - { - code: "LB", - name: "Lebanon", - }, - { - code: "MO", - name: "Macao", - }, - { - code: "MK", - name: "North Macedonia", - }, - { - code: "MG", - name: "Madagascar", - }, - { - code: "MY", - name: "Malaysia", - }, - { - code: "MW", - name: "Malawi", - }, - { - code: "MV", - name: "Maldives", - }, - { - code: "ML", - name: "Mali", - }, - { - code: "MT", - name: "Malta", - }, - { - code: "MA", - name: "Morocco", - }, - { - code: "MQ", - name: "Martinique", - }, - { - code: "MU", - name: "Mauritius", - }, - { - code: "MR", - name: "Mauritania", - }, - { - code: "YT", - name: "Mayotte", - }, - { - code: "FM", - name: "Micronesia", - }, - { - code: "MD", - name: "Moldova", - }, - { - code: "MN", - name: "Mongolia", - }, - { - code: "ME", - name: "Montenegro", - }, - { - code: "MS", - name: "Montserrat", - }, - { - code: "MZ", - name: "Mozambique", - }, - { - code: "MM", - name: "Myanmar [Burma]", - }, - { - code: "MX", - name: "Mexico", - }, - { - code: "MC", - name: "Monaco", - }, - { - code: "NA", - name: "Namibia", - }, - { - code: "NR", - name: "Nauru", - }, - { - code: "NP", - name: "Nepal", - }, - { - code: "NI", - name: "Nicaragua", - }, - { - code: "NG", - name: "Nigeria", - }, - { - code: "NO", - name: "Norway", - }, - { - code: "NC", - name: "New Caledonia", - }, - { - code: "NZ", - name: "New Zealand", - }, - { - code: "NE", - name: "Niger", - }, - { - code: "OM", - name: "Oman", - }, - { - code: "PK", - name: "Pakistan", - }, - { - code: "PW", - name: "Palau", - }, - { - code: "PA", - name: "Panama", - }, - { - code: "PG", - name: "Papua New Guinea", - }, - { - code: "PY", - name: "Paraguay", - }, - { - code: "PE", - name: "Peru", - }, - { - code: "PF", - name: "French Polynesia", - }, - { - code: "PL", - name: "Poland", - }, - { - code: "PT", - name: "Portugal", - }, - { - code: "PR", - name: "Puerto Rico", - }, - { - code: "QA", - name: "Qatar", - }, - { - code: "GB", - name: "United Kingdom", - }, - { - code: "CF", - name: "Central African Republic", - }, - { - code: "CZ", - name: "Czechia", - }, - { - code: "CD", - name: "Democratic Republic of the Congo", - }, - { - code: "DO", - name: "Dominican Republic", - }, - { - code: "RE", - name: "Réunion", - }, - { - code: "RW", - name: "Rwanda", - }, - { - code: "RO", - name: "Romania", - }, - { - code: "RU", - name: "Russia", - }, - { - code: "WS", - name: "Samoa", - }, - { - code: "AS", - name: "American Samoa", - }, - { - code: "BL", - name: "Saint Barthélemy", - }, - { - code: "KN", - name: "Saint Kitts and Nevis", - }, - { - code: "SM", - name: "San Marino", - }, - { - code: "MF", - name: "Saint Martin", - }, - { - code: "SX", - name: "Sint Maarten", - }, - { - code: "PM", - name: "Saint Pierre and Miquelon", - }, - { - code: "VC", - name: "Saint Vincent and the Grenadines", - }, - { - code: "SH", - name: "Saint Helena", - }, - { - code: "LC", - name: "Saint Lucia", - }, - { - code: "ST", - name: "São Tomé and Príncipe", - }, - { - code: "SN", - name: "Senegal", - }, - { - code: "RS", - name: "Serbia", - }, - { - code: "SC", - name: "Seychelles", - }, - { - code: "SL", - name: "Sierra Leone", - }, - { - code: "SG", - name: "Singapore", - }, - { - code: "SY", - name: "Syria", - }, - { - code: "SO", - name: "Somalia", - }, - { - code: "LK", - name: "Sri Lanka", - }, - { - code: "SZ", - name: "Eswatini", - }, - { - code: "ZA", - name: "South Africa", - }, - { - code: "SD", - name: "Sudan", - }, - { - code: "SS", - name: "South Sudan", - }, - { - code: "SE", - name: "Sweden", - }, - { - code: "CH", - name: "Switzerland", - }, - { - code: "SR", - name: "Suriname", - }, - { - code: "SJ", - name: "Svalbard and Jan Mayen", - }, - { - code: "EH", - name: "Western Sahara", - }, - { - code: "TH", - name: "Thailand", - }, - { - code: "TW", - name: "Taiwan", - }, - { - code: "TZ", - name: "Tanzania", - }, - { - code: "TJ", - name: "Tajikistan", - }, - { - code: "IO", - name: "British Indian Ocean Territory", - }, - { - code: "TF", - name: "French Southern Territories", - }, - { - code: "PS", - name: "Palestine", - }, - { - code: "TL", - name: "Timor-Leste", - }, - { - code: "TG", - name: "Togo", - }, - { - code: "TK", - name: "Tokelau", - }, - { - code: "TO", - name: "Tonga", - }, - { - code: "TT", - name: "Trinidad and Tobago", - }, - { - code: "TM", - name: "Turkmenistan", - }, - { - code: "TR", - name: "Turkey", - }, - { - code: "TV", - name: "Tuvalu", - }, - { - code: "TN", - name: "Tunisia", - }, - { - code: "UA", - name: "Ukraine", - }, - { - code: "UG", - name: "Uganda", - }, - { - code: "UY", - name: "Uruguay", - }, - { - code: "UZ", - name: "Uzbekistan", - }, - { - code: "VU", - name: "Vanuatu", - }, - { - code: "VE", - name: "Venezuela", - }, - { - code: "VN", - name: "Vietnam", - }, - { - code: "WF", - name: "Wallis and Futuna", - }, - { - code: "YE", - name: "Yemen", - }, - { - code: "DJ", - name: "Djibouti", - }, - { - code: "ZM", - name: "Zambia", - }, - { - code: "ZW", - name: "Zimbabwe", - }, + { + code: "AF", + name: "Afghanistan", + }, + { + code: "AL", + name: "Albania", + }, + { + code: "DE", + name: "Germany", + }, + { + code: "AD", + name: "Andorra", + }, + { + code: "AO", + name: "Angola", + }, + { + code: "AI", + name: "Anguilla", + }, + { + code: "AG", + name: "Antigua and Barbuda", + }, + { + code: "AQ", + name: "Antarctica", + }, + { + code: "SA", + name: "Saudi Arabia", + }, + { + code: "DZ", + name: "Algeria", + }, + { + code: "AR", + name: "Argentina", + }, + { + code: "AM", + name: "Armenia", + }, + { + code: "AW", + name: "Aruba", + }, + { + code: "AU", + name: "Australia", + }, + { + code: "AT", + name: "Austria", + }, + { + code: "AZ", + name: "Azerbaijan", + }, + { + code: "BS", + name: "Bahamas", + }, + { + code: "BH", + name: "Bahrain", + }, + { + code: "BD", + name: "Bangladesh", + }, + { + code: "BB", + name: "Barbados", + }, + { + code: "BZ", + name: "Belize", + }, + { + code: "BJ", + name: "Benin", + }, + { + code: "BM", + name: "Bermuda", + }, + { + code: "BY", + name: "Belarus", + }, + { + code: "BO", + name: "Bolivia", + }, + { + code: "BQ", + name: "Bonaire", + }, + { + code: "BA", + name: "Bosnia and Herzegovina", + }, + { + code: "BW", + name: "Botswana", + }, + { + code: "BR", + name: "Brazil", + }, + { + code: "BN", + name: "Brunei", + }, + { + code: "BG", + name: "Bulgaria", + }, + { + code: "BF", + name: "Burkina Faso", + }, + { + code: "BI", + name: "Burundi", + }, + { + code: "BT", + name: "Bhutan", + }, + { + code: "BE", + name: "Belgium", + }, + { + code: "CV", + name: "Cabo Verde", + }, + { + code: "KH", + name: "Cambodia", + }, + { + code: "CM", + name: "Cameroon", + }, + { + code: "CA", + name: "Canada", + }, + { + code: "TD", + name: "Chad", + }, + { + code: "CL", + name: "Chile", + }, + { + code: "CN", + name: "China", + }, + { + code: "CY", + name: "Cyprus", + }, + { + code: "VA", + name: "Vatican City", + }, + { + code: "CO", + name: "Colombia", + }, + { + code: "KM", + name: "Comoros", + }, + { + code: "CG", + name: "Republic of the Congo", + }, + { + code: "KP", + name: "North Korea", + }, + { + code: "KR", + name: "South Korea", + }, + { + code: "CR", + name: "Costa Rica", + }, + { + code: "CI", + name: "Ivory Coast", + }, + { + code: "HR", + name: "Croatia", + }, + { + code: "CU", + name: "Cuba", + }, + { + code: "CW", + name: "Curacao", + }, + { + code: "DK", + name: "Denmark", + }, + { + code: "DM", + name: "Dominica", + }, + { + code: "EC", + name: "Ecuador", + }, + { + code: "EG", + name: "Egypt", + }, + { + code: "SV", + name: "El Salvador", + }, + { + code: "AE", + name: "United Arab Emirates", + }, + { + code: "ER", + name: "Eritrea", + }, + { + code: "SK", + name: "Slovakia", + }, + { + code: "SI", + name: "Slovenia", + }, + { + code: "ES", + name: "Spain", + }, + { + code: "US", + name: "United States", + }, + { + code: "EE", + name: "Estonia", + }, + { + code: "ET", + name: "Ethiopia", + }, + { + code: "PH", + name: "Philippines", + }, + { + code: "FI", + name: "Finland", + }, + { + code: "FJ", + name: "Fiji", + }, + { + code: "FR", + name: "France", + }, + { + code: "GA", + name: "Gabon", + }, + { + code: "GM", + name: "Gambia", + }, + { + code: "GE", + name: "Georgia", + }, + { + code: "GH", + name: "Ghana", + }, + { + code: "GI", + name: "Gibraltar", + }, + { + code: "GD", + name: "Grenada", + }, + { + code: "GR", + name: "Greece", + }, + { + code: "GL", + name: "Greenland", + }, + { + code: "GP", + name: "Guadeloupe", + }, + { + code: "GU", + name: "Guam", + }, + { + code: "GT", + name: "Guatemala", + }, + { + code: "GF", + name: "French Guiana", + }, + { + code: "GG", + name: "Guernsey", + }, + { + code: "GN", + name: "Guinea", + }, + { + code: "GQ", + name: "Equatorial Guinea", + }, + { + code: "GW", + name: "Guinea-Bissau", + }, + { + code: "GY", + name: "Guyana", + }, + { + code: "HT", + name: "Haiti", + }, + { + code: "NL", + name: "Netherlands", + }, + { + code: "HN", + name: "Honduras", + }, + { + code: "HK", + name: "Hong Kong", + }, + { + code: "HU", + name: "Hungary", + }, + { + code: "IN", + name: "India", + }, + { + code: "ID", + name: "Indonesia", + }, + { + code: "IQ", + name: "Iraq", + }, + { + code: "IE", + name: "Ireland", + }, + { + code: "IR", + name: "Iran", + }, + { + code: "BV", + name: "Bouvet Island", + }, + { + code: "CX", + name: "Christmas Island", + }, + { + code: "NU", + name: "Niue", + }, + { + code: "NF", + name: "Norfolk Island", + }, + { + code: "IM", + name: "Isle of Man", + }, + { + code: "IS", + name: "Iceland", + }, + { + code: "KY", + name: "Cayman Islands", + }, + { + code: "CC", + name: "Cocos [Keeling] Islands", + }, + { + code: "CK", + name: "Cook Islands", + }, + { + code: "FO", + name: "Faroe Islands", + }, + { + code: "GS", + name: "South Georgia and the South Sandwich Islands", + }, + { + code: "HM", + name: "Heard Island and McDonald Islands", + }, + { + code: "FK", + name: "Falkland Islands", + }, + { + code: "MP", + name: "Northern Mariana Islands", + }, + { + code: "MH", + name: "Marshall Islands", + }, + { + code: "PN", + name: "Pitcairn Islands", + }, + { + code: "SB", + name: "Solomon Islands", + }, + { + code: "TC", + name: "Turks and Caicos Islands", + }, + { + code: "VG", + name: "British Virgin Islands", + }, + { + code: "VI", + name: "U.S. Virgin Islands", + }, + { + code: "AX", + name: "Åland", + }, + { + code: "UM", + name: "U.S. Minor Outlying Islands", + }, + { + code: "IL", + name: "Israel", + }, + { + code: "IT", + name: "Italy", + }, + { + code: "JM", + name: "Jamaica", + }, + { + code: "JP", + name: "Japan", + }, + { + code: "JE", + name: "Jersey", + }, + { + code: "JO", + name: "Jordan", + }, + { + code: "KZ", + name: "Kazakhstan", + }, + { + code: "KE", + name: "Kenya", + }, + { + code: "KG", + name: "Kyrgyzstan", + }, + { + code: "KI", + name: "Kiribati", + }, + { + code: "XK", + name: "Kosovo", + }, + { + code: "KW", + name: "Kuwait", + }, + { + code: "LA", + name: "Laos", + }, + { + code: "LS", + name: "Lesotho", + }, + { + code: "LV", + name: "Latvia", + }, + { + code: "LR", + name: "Liberia", + }, + { + code: "LY", + name: "Libya", + }, + { + code: "LI", + name: "Liechtenstein", + }, + { + code: "LT", + name: "Lithuania", + }, + { + code: "LU", + name: "Luxembourg", + }, + { + code: "LB", + name: "Lebanon", + }, + { + code: "MO", + name: "Macao", + }, + { + code: "MK", + name: "North Macedonia", + }, + { + code: "MG", + name: "Madagascar", + }, + { + code: "MY", + name: "Malaysia", + }, + { + code: "MW", + name: "Malawi", + }, + { + code: "MV", + name: "Maldives", + }, + { + code: "ML", + name: "Mali", + }, + { + code: "MT", + name: "Malta", + }, + { + code: "MA", + name: "Morocco", + }, + { + code: "MQ", + name: "Martinique", + }, + { + code: "MU", + name: "Mauritius", + }, + { + code: "MR", + name: "Mauritania", + }, + { + code: "YT", + name: "Mayotte", + }, + { + code: "FM", + name: "Micronesia", + }, + { + code: "MD", + name: "Moldova", + }, + { + code: "MN", + name: "Mongolia", + }, + { + code: "ME", + name: "Montenegro", + }, + { + code: "MS", + name: "Montserrat", + }, + { + code: "MZ", + name: "Mozambique", + }, + { + code: "MM", + name: "Myanmar [Burma]", + }, + { + code: "MX", + name: "Mexico", + }, + { + code: "MC", + name: "Monaco", + }, + { + code: "NA", + name: "Namibia", + }, + { + code: "NR", + name: "Nauru", + }, + { + code: "NP", + name: "Nepal", + }, + { + code: "NI", + name: "Nicaragua", + }, + { + code: "NG", + name: "Nigeria", + }, + { + code: "NO", + name: "Norway", + }, + { + code: "NC", + name: "New Caledonia", + }, + { + code: "NZ", + name: "New Zealand", + }, + { + code: "NE", + name: "Niger", + }, + { + code: "OM", + name: "Oman", + }, + { + code: "PK", + name: "Pakistan", + }, + { + code: "PW", + name: "Palau", + }, + { + code: "PA", + name: "Panama", + }, + { + code: "PG", + name: "Papua New Guinea", + }, + { + code: "PY", + name: "Paraguay", + }, + { + code: "PE", + name: "Peru", + }, + { + code: "PF", + name: "French Polynesia", + }, + { + code: "PL", + name: "Poland", + }, + { + code: "PT", + name: "Portugal", + }, + { + code: "PR", + name: "Puerto Rico", + }, + { + code: "QA", + name: "Qatar", + }, + { + code: "GB", + name: "United Kingdom", + }, + { + code: "CF", + name: "Central African Republic", + }, + { + code: "CZ", + name: "Czechia", + }, + { + code: "CD", + name: "Democratic Republic of the Congo", + }, + { + code: "DO", + name: "Dominican Republic", + }, + { + code: "RE", + name: "Réunion", + }, + { + code: "RW", + name: "Rwanda", + }, + { + code: "RO", + name: "Romania", + }, + { + code: "RU", + name: "Russia", + }, + { + code: "WS", + name: "Samoa", + }, + { + code: "AS", + name: "American Samoa", + }, + { + code: "BL", + name: "Saint Barthélemy", + }, + { + code: "KN", + name: "Saint Kitts and Nevis", + }, + { + code: "SM", + name: "San Marino", + }, + { + code: "MF", + name: "Saint Martin", + }, + { + code: "SX", + name: "Sint Maarten", + }, + { + code: "PM", + name: "Saint Pierre and Miquelon", + }, + { + code: "VC", + name: "Saint Vincent and the Grenadines", + }, + { + code: "SH", + name: "Saint Helena", + }, + { + code: "LC", + name: "Saint Lucia", + }, + { + code: "ST", + name: "São Tomé and Príncipe", + }, + { + code: "SN", + name: "Senegal", + }, + { + code: "RS", + name: "Serbia", + }, + { + code: "SC", + name: "Seychelles", + }, + { + code: "SL", + name: "Sierra Leone", + }, + { + code: "SG", + name: "Singapore", + }, + { + code: "SY", + name: "Syria", + }, + { + code: "SO", + name: "Somalia", + }, + { + code: "LK", + name: "Sri Lanka", + }, + { + code: "SZ", + name: "Eswatini", + }, + { + code: "ZA", + name: "South Africa", + }, + { + code: "SD", + name: "Sudan", + }, + { + code: "SS", + name: "South Sudan", + }, + { + code: "SE", + name: "Sweden", + }, + { + code: "CH", + name: "Switzerland", + }, + { + code: "SR", + name: "Suriname", + }, + { + code: "SJ", + name: "Svalbard and Jan Mayen", + }, + { + code: "EH", + name: "Western Sahara", + }, + { + code: "TH", + name: "Thailand", + }, + { + code: "TW", + name: "Taiwan", + }, + { + code: "TZ", + name: "Tanzania", + }, + { + code: "TJ", + name: "Tajikistan", + }, + { + code: "IO", + name: "British Indian Ocean Territory", + }, + { + code: "TF", + name: "French Southern Territories", + }, + { + code: "PS", + name: "Palestine", + }, + { + code: "TL", + name: "Timor-Leste", + }, + { + code: "TG", + name: "Togo", + }, + { + code: "TK", + name: "Tokelau", + }, + { + code: "TO", + name: "Tonga", + }, + { + code: "TT", + name: "Trinidad and Tobago", + }, + { + code: "TM", + name: "Turkmenistan", + }, + { + code: "TR", + name: "Turkey", + }, + { + code: "TV", + name: "Tuvalu", + }, + { + code: "TN", + name: "Tunisia", + }, + { + code: "UA", + name: "Ukraine", + }, + { + code: "UG", + name: "Uganda", + }, + { + code: "UY", + name: "Uruguay", + }, + { + code: "UZ", + name: "Uzbekistan", + }, + { + code: "VU", + name: "Vanuatu", + }, + { + code: "VE", + name: "Venezuela", + }, + { + code: "VN", + name: "Vietnam", + }, + { + code: "WF", + name: "Wallis and Futuna", + }, + { + code: "YE", + name: "Yemen", + }, + { + code: "DJ", + name: "Djibouti", + }, + { + code: "ZM", + name: "Zambia", + }, + { + code: "ZW", + name: "Zimbabwe", + }, ]; diff --git a/src/configs/hotkeysConfig.ts b/src/configs/hotkeysConfig.ts index 544864e97d..a1eac89036 100644 --- a/src/configs/hotkeysConfig.ts +++ b/src/configs/hotkeysConfig.ts @@ -1,47 +1,47 @@ // keymap containing information about available hotkeys type HotkeyMapType = { - [key: string]: { - [key: string]: { - name: string, - description: string, - sequence: string[], - } - } + [key: string]: { + [key: string]: { + name: string, + description: string, + sequence: string[], + } + } } export const availableHotkeys: HotkeyMapType = { - general: { - HOTKEY_CHEATSHEET: { - name: "hotkey_cheatsheet", - description: "HOTKEYS.DESCRIPTIONS.GENERAL.CHEAT_SHEET", - sequence: ["h"], - }, - EVENT_VIEW: { - name: "event_view", - description: "HOTKEYS.DESCRIPTIONS.GENERAL.EVENT_VIEW", - sequence: ["e"], - }, - SERIES_VIEW: { - name: "series_view", - description: "HOTKEYS.DESCRIPTIONS.GENERAL.SERIES_VIEW", - sequence: ["s"], - }, - NEW_EVENT: { - name: "new_event", - description: "HOTKEYS.DESCRIPTIONS.GENERAL.NEW_EVENT", - sequence: ["n"], - }, - NEW_SERIES: { - name: "new_series", - description: "HOTKEYS.DESCRIPTIONS.GENERAL.NEW_SERIES", - sequence: ["j"], - }, - MAIN_MENU: { - name: "main_menu", - description: "HOTKEYS.DESCRIPTIONS.GENERAL.MAIN_MENU", - sequence: ["m"], - }, - /*NEXT_DASHBOARD_FILTER: { + general: { + HOTKEY_CHEATSHEET: { + name: "hotkey_cheatsheet", + description: "HOTKEYS.DESCRIPTIONS.GENERAL.CHEAT_SHEET", + sequence: ["h"], + }, + EVENT_VIEW: { + name: "event_view", + description: "HOTKEYS.DESCRIPTIONS.GENERAL.EVENT_VIEW", + sequence: ["e"], + }, + SERIES_VIEW: { + name: "series_view", + description: "HOTKEYS.DESCRIPTIONS.GENERAL.SERIES_VIEW", + sequence: ["s"], + }, + NEW_EVENT: { + name: "new_event", + description: "HOTKEYS.DESCRIPTIONS.GENERAL.NEW_EVENT", + sequence: ["n"], + }, + NEW_SERIES: { + name: "new_series", + description: "HOTKEYS.DESCRIPTIONS.GENERAL.NEW_SERIES", + sequence: ["j"], + }, + MAIN_MENU: { + name: "main_menu", + description: "HOTKEYS.DESCRIPTIONS.GENERAL.MAIN_MENU", + sequence: ["m"], + }, + /*NEXT_DASHBOARD_FILTER: { name: 'select_next_dashboard_filter', description: 'HOTKEYS.DESCRIPTIONS.GENERAL.SELECT_NEXT_DASHBOARD_FILTER', combo: ['f'], @@ -57,15 +57,15 @@ export const availableHotkeys: HotkeyMapType = { action: 'keyup', allowIn: ['INPUT', 'SELECT', 'TEXTAREA'] },*/ - REMOVE_FILTERS: { - name: "remove_filters", - description: "HOTKEYS.DESCRIPTIONS.GENERAL.REMOVE_FILTERS", - sequence: ["r"], - }, - CLOSE_MODAL: { - name: "close_modal", - description: "HOTKEYS.DESCRIPTIONS.GENERAL.CLOSE_MODAL", - sequence: ["Esc"], - } - }, + REMOVE_FILTERS: { + name: "remove_filters", + description: "HOTKEYS.DESCRIPTIONS.GENERAL.REMOVE_FILTERS", + sequence: ["r"], + }, + CLOSE_MODAL: { + name: "close_modal", + description: "HOTKEYS.DESCRIPTIONS.GENERAL.CLOSE_MODAL", + sequence: ["Esc"], + } + }, }; diff --git a/src/configs/modalConfig.ts b/src/configs/modalConfig.ts index a4e56f9436..efc97530e0 100644 --- a/src/configs/modalConfig.ts +++ b/src/configs/modalConfig.ts @@ -16,40 +16,40 @@ export const NOTIFICATION_CONTEXT_ACCESS = "wizard-access"; export const NOTIFICATION_CONTEXT_TOBIRA = "tobira"; export const initialFormValuesNewEvents: { - sourceMode: string, - scheduleStartDate: string, - scheduleEndDate: string, - scheduleStartHour: string, - scheduleStartMinute: string, - scheduleDurationHours: string, - scheduleDurationMinutes: string, - scheduleEndHour: string, - scheduleEndMinute: string, - repeatOn: string[], - location: string, - processingWorkflow: string, - configuration: { [key: string]: string }, - aclTemplate: string, - acls: TransformedAcl[], - uploadAssetsTrack?: UploadAssetsTrack[] - [key: string]: unknown, // Metadata fields that are getting added later + sourceMode: string, + scheduleStartDate: string, + scheduleEndDate: string, + scheduleStartHour: string, + scheduleStartMinute: string, + scheduleDurationHours: string, + scheduleDurationMinutes: string, + scheduleEndHour: string, + scheduleEndMinute: string, + repeatOn: string[], + location: string, + processingWorkflow: string, + configuration: { [key: string]: string }, + aclTemplate: string, + acls: TransformedAcl[], + uploadAssetsTrack?: UploadAssetsTrack[] + [key: string]: unknown, // Metadata fields that are getting added later } = { - sourceMode: "UPLOAD", - scheduleStartDate: new Date().toISOString(), - scheduleEndDate: new Date().toISOString(), - scheduleStartHour: "", - scheduleStartMinute: "", - scheduleDurationHours: "", - scheduleDurationMinutes: "", - scheduleEndHour: "", - scheduleEndMinute: "", - repeatOn: [], - location: "", - //deviceInputs: [], - processingWorkflow: "", - configuration: {}, - aclTemplate: "", - acls: [], + sourceMode: "UPLOAD", + scheduleStartDate: new Date().toISOString(), + scheduleEndDate: new Date().toISOString(), + scheduleStartHour: "", + scheduleStartMinute: "", + scheduleDurationHours: "", + scheduleDurationMinutes: "", + scheduleEndHour: "", + scheduleEndMinute: "", + repeatOn: [], + location: "", + //deviceInputs: [], + processingWorkflow: "", + configuration: {}, + aclTemplate: "", + acls: [], }; // constants for hours and minutes (used in selection for start/end time and duration) @@ -58,34 +58,34 @@ export const minutes = initArray(60); // sorted weekdays and their translation key export const weekdays = [ - { - name: "MO", - label: "EVENTS.EVENTS.NEW.WEEKDAYS.MO", - }, - { - name: "TU", - label: "EVENTS.EVENTS.NEW.WEEKDAYS.TU", - }, - { - name: "WE", - label: "EVENTS.EVENTS.NEW.WEEKDAYS.WE", - }, - { - name: "TH", - label: "EVENTS.EVENTS.NEW.WEEKDAYS.TH", - }, - { - name: "FR", - label: "EVENTS.EVENTS.NEW.WEEKDAYS.FR", - }, - { - name: "SA", - label: "EVENTS.EVENTS.NEW.WEEKDAYS.SA", - }, - { - name: "SU", - label: "EVENTS.EVENTS.NEW.WEEKDAYS.SU", - }, + { + name: "MO", + label: "EVENTS.EVENTS.NEW.WEEKDAYS.MO", + }, + { + name: "TU", + label: "EVENTS.EVENTS.NEW.WEEKDAYS.TU", + }, + { + name: "WE", + label: "EVENTS.EVENTS.NEW.WEEKDAYS.WE", + }, + { + name: "TH", + label: "EVENTS.EVENTS.NEW.WEEKDAYS.TH", + }, + { + name: "FR", + label: "EVENTS.EVENTS.NEW.WEEKDAYS.FR", + }, + { + name: "SA", + label: "EVENTS.EVENTS.NEW.WEEKDAYS.SA", + }, + { + name: "SU", + label: "EVENTS.EVENTS.NEW.WEEKDAYS.SU", + }, ]; // Workflow applied to upload assets that are not tracks @@ -94,119 +94,119 @@ export const WORKFLOW_UPLOAD_ASSETS_NON_TRACK = "publish-uploaded-assets"; // All fields for new series form that are fix and not depending on response of backend // InitialValues of Formik form (others computed dynamically depending on responses from backend) export const initialFormValuesNewSeries: { - acls: TransformedAcl[], - theme: string, + acls: TransformedAcl[], + theme: string, - breadcrumbs: TobiraPage[], - selectedPage?: TobiraPage, - [key: string]: unknown, // Metadata fields that are getting added later + breadcrumbs: TobiraPage[], + selectedPage?: TobiraPage, + [key: string]: unknown, // Metadata fields that are getting added later } = { - acls: [ - { - role: "ROLE_USER_ADMIN", - read: true, - write: true, - actions: [], - }, - ], - theme: "", - breadcrumbs: [], - selectedPage: undefined, + acls: [ + { + role: "ROLE_USER_ADMIN", + read: true, + write: true, + actions: [], + }, + ], + theme: "", + breadcrumbs: [], + selectedPage: undefined, }; // All fields for new theme form that are fix and not depending on response of backend // InitialValues of Formik form (others computed dynamically depending on responses from backend) export const initialFormValuesNewThemes = { - name: "", - description: "", - bumperActive: false, - bumperFile: "", - bumperFileName: "", - trailerActive: false, - trailerFile: "", - trailerFileName: "", - titleSlideActive: false, - titleSlideMode: "extract", - titleSlideBackground: "", - titleSlideBackgroundName: "", - licenseSlideActive: false, - watermarkActive: false, - watermarkFile: "", - watermarkFileName: "", - watermarkPosition: "topRight", - - // Don't care about these, but they are required by type - creationDate: "", - creator: "", - default: false, - id: 0, - licenseSlideBackground: "", - licenseSlideDescription: "", - titleSlideMetadata: "", + name: "", + description: "", + bumperActive: false, + bumperFile: "", + bumperFileName: "", + trailerActive: false, + trailerFile: "", + trailerFileName: "", + titleSlideActive: false, + titleSlideMode: "extract", + titleSlideBackground: "", + titleSlideBackgroundName: "", + licenseSlideActive: false, + watermarkActive: false, + watermarkFile: "", + watermarkFileName: "", + watermarkPosition: "topRight", + + // Don't care about these, but they are required by type + creationDate: "", + creator: "", + default: false, + id: 0, + licenseSlideBackground: "", + licenseSlideDescription: "", + titleSlideMetadata: "", }; // All fields for new acl form that are fix and not depending on response of backend // InitialValues of Formik form (others computed dynamically depending on responses from backend) export const initialFormValuesNewAcl: { - name: string, - acls: TransformedAcl[], + name: string, + acls: TransformedAcl[], } = { - name: "", - acls: [], + name: "", + acls: [], }; // All fields for new group form that are fix and not depending on response of backend // InitialValues of Formik form (others computed dynamically depending on responses from backend) export const initialFormValuesNewGroup: { - name: string, - description: string, - roles: { name: string }[], - users: { id: string, name: string }[], + name: string, + description: string, + roles: { name: string }[], + users: { id: string, name: string }[], } = { - name: "", - description: "", - roles: [], - users: [], + name: "", + description: "", + roles: [], + users: [], }; // All fields for new user form that are fix and not depending on response of backend // InitialValues of Formik form (others computed dynamically depending on responses from backend) export const initialFormValuesNewUser: { - username: string, - name: string, - email: string, - password: string, - passwordConfirmation: string, - roles: Role[], - manageable: boolean, + username: string, + name: string, + email: string, + password: string, + passwordConfirmation: string, + roles: Role[], + manageable: boolean, } = { - username: "", - name: "", - email: "", - password: "", - passwordConfirmation: "", - roles: [], - manageable: true, + username: "", + name: "", + email: "", + password: "", + passwordConfirmation: "", + roles: [], + manageable: true, }; // All fields for start task form that are fix and not depending on response of backend // InitialValues of Formik form (others computed dynamically depending on responses from backend) export const initialFormValuesStartTask: { - events: Event[], - workflow: string, - configuration: { [key: string]: string }, + events: Event[], + workflow: string, + configuration: { [key: string]: string }, } = { - events: [], - workflow: "", - configuration: {}, + events: [], + workflow: "", + configuration: {}, }; export const initialFormValuesEditScheduledEvents: { - events: Event[], - editedEvents: EditedEvents[], - changedEvents: string[], + events: Event[], + editedEvents: EditedEvents[], + changedEvents: string[], } = { - events: [], - editedEvents: [], - changedEvents: [], + events: [], + editedEvents: [], + changedEvents: [], }; diff --git a/src/configs/sourceConfig.ts b/src/configs/sourceConfig.ts index 78de51ee0f..46d081b685 100644 --- a/src/configs/sourceConfig.ts +++ b/src/configs/sourceConfig.ts @@ -12,22 +12,22 @@ import { MetadataField } from "../slices/eventSlice"; */ type SourceType = { - UPLOAD?: { metadata: MetadataField[] }, - SCHEDULE_SINGLE?: { metadata: MetadataField[] }, - SCHEDULE_MULTIPLE?: { metadata: MetadataField[] }, + UPLOAD?: { metadata: MetadataField[] }, + SCHEDULE_SINGLE?: { metadata: MetadataField[] }, + SCHEDULE_MULTIPLE?: { metadata: MetadataField[] }, } export const sourceMetadata: SourceType = { - UPLOAD: { - metadata: [ - { - id: "startDate", - label: "EVENTS.EVENTS.DETAILS.METADATA.START_DATE", - value: new Date(Date.now()).toISOString(), - type: "date", - readOnly: false, - required: false, - }, - ], - }, + UPLOAD: { + metadata: [ + { + id: "startDate", + label: "EVENTS.EVENTS.DETAILS.METADATA.START_DATE", + value: new Date(Date.now()).toISOString(), + type: "date", + readOnly: false, + required: false, + }, + ], + }, }; diff --git a/src/configs/statisticsConfig.ts b/src/configs/statisticsConfig.ts index 2b6cc736f8..6029b5bec3 100644 --- a/src/configs/statisticsConfig.ts +++ b/src/configs/statisticsConfig.ts @@ -2,41 +2,41 @@ // available modes of choosing statistic timeframe export const statisticTimeModes = [ - { - value: "year", - translation: "Year", - }, - { - value: "month", - translation: "Month", - }, - { - value: "custom", - translation: "Custom", - }, + { + value: "year", + translation: "Year", + }, + { + value: "month", + translation: "Month", + }, + { + value: "custom", + translation: "Custom", + }, ]; // data resolutions (or time granularity) for statistics with year or month timeframe export const fixedStatisticDataResolutions = (timeMode: "month" | "year") => { - if (timeMode === "month") { - return "daily" - } - if (timeMode === "year") { - return "monthly" - } - return "monthly" + if (timeMode === "month") { + return "daily" + } + if (timeMode === "year") { + return "monthly" + } + return "monthly" }; // available data resolutions (or time granularity) for statistics with custom timeframe export const availableCustomStatisticDataResolutions = [ - { label: "Yearly", value: "yearly" }, - { label: "Monthly", value: "monthly" }, - { label: "Daily", value: "daily" }, - { label: "Hourly", value: "hourly" }, + { label: "Yearly", value: "yearly" }, + { label: "Monthly", value: "monthly" }, + { label: "Daily", value: "daily" }, + { label: "Hourly", value: "hourly" }, ]; // date format strings export const statisticDateFormatStrings = { - month: "MMMM YYYY", - year: "YYYY", + month: "MMMM YYYY", + year: "YYYY", }; diff --git a/src/configs/tableConfigs/aclsTableConfig.ts b/src/configs/tableConfigs/aclsTableConfig.ts index 1f9d9f08bd..4a74c16b71 100644 --- a/src/configs/tableConfigs/aclsTableConfig.ts +++ b/src/configs/tableConfigs/aclsTableConfig.ts @@ -1,18 +1,18 @@ export type TableConfig = { - columns: TableColumn[], - caption: string, - resource: string, - category: string, - multiSelect: boolean, + columns: TableColumn[], + caption: string, + resource: string, + category: string, + multiSelect: boolean, } export type TableColumn = { - name: string, - label: string, - template?: string, - sortable?: boolean, - translate?: boolean, - deactivated?: boolean, + name: string, + label: string, + template?: string, + sortable?: boolean, + translate?: boolean, + deactivated?: boolean, } /** @@ -26,20 +26,20 @@ export type TableColumn = { * - is multi select possible? */ export const aclsTableConfig: TableConfig = { - columns: [ - { - name: "name", - label: "USERS.ACLS.TABLE.NAME", - sortable: true, - }, - { - template: "AclsActionsCell", - name: "actions", - label: "USERS.ACLS.TABLE.ACTION", - }, - ], - caption: "USERS.ACLS.TABLE.CAPTION", - resource: "acls", - category: "users", - multiSelect: false, + columns: [ + { + name: "name", + label: "USERS.ACLS.TABLE.NAME", + sortable: true, + }, + { + template: "AclsActionsCell", + name: "actions", + label: "USERS.ACLS.TABLE.ACTION", + }, + ], + caption: "USERS.ACLS.TABLE.CAPTION", + resource: "acls", + category: "users", + multiSelect: false, }; diff --git a/src/configs/tableConfigs/aclsTableMap.ts b/src/configs/tableConfigs/aclsTableMap.ts index 6ec0834689..8a2087131a 100644 --- a/src/configs/tableConfigs/aclsTableMap.ts +++ b/src/configs/tableConfigs/aclsTableMap.ts @@ -6,5 +6,5 @@ import AclsActionsCell from "../../components/users/partials/AclsActionsCell"; * uses template map. */ export const aclsTemplateMap = { - AclsActionsCell: AclsActionsCell, + AclsActionsCell: AclsActionsCell, }; diff --git a/src/configs/tableConfigs/eventsTableConfig.ts b/src/configs/tableConfigs/eventsTableConfig.ts index e09eab8747..020922ef4f 100644 --- a/src/configs/tableConfigs/eventsTableConfig.ts +++ b/src/configs/tableConfigs/eventsTableConfig.ts @@ -11,84 +11,84 @@ import { TableConfig } from "./aclsTableConfig"; * - is multi select possible? */ export const eventsTableConfig: TableConfig = { - columns: [ - { - name: "title", - label: "EVENTS.EVENTS.TABLE.TITLE", - sortable: true, - translate: false, - }, - { - template: "EventsPresentersCell", - name: "presenter", - label: "EVENTS.EVENTS.TABLE.PRESENTERS", - sortable: true, - translate: false, - }, - { - template: "EventsSeriesCell", - name: "series_name", - label: "EVENTS.EVENTS.TABLE.SERIES", - sortable: true, - translate: false, - }, - { - template: "EventsDateCell", - name: "date", - label: "EVENTS.EVENTS.TABLE.DATE", - sortable: true, - translate: false, - }, - { - template: "EventsStartCell", - name: "start_date", - label: "EVENTS.EVENTS.TABLE.START", - sortable: true, - translate: false, - }, - { - template: "EventsEndCell", - name: "end_date", - label: "EVENTS.EVENTS.TABLE.STOP", - sortable: true, - translate: false, - }, - { - template: "EventsLocationCell", - name: "location", - label: "EVENTS.EVENTS.TABLE.LOCATION", - sortable: true, - translate: false, - }, - { - name: "published", - label: "EVENTS.EVENTS.TABLE.PUBLISHED", - template: "PublishedCell", - translate: false, - }, - { - template: "EventsStatusCell", - name: "event_status", - label: "EVENTS.EVENTS.TABLE.STATUS", - sortable: true, - translate: true, - }, - { - name: "actions", - template: "EventActionsCell", - label: "EVENTS.EVENTS.TABLE.ACTION", - translate: false, - }, - { - name: "notes", - template: "EventsNotesCell", - label: "EVENTS.EVENTS.TABLE.ADMINUI_NOTES", - translate: false, - deactivated: true, - }, - ], - caption: "EVENTS.EVENTS.TABLE.CAPTION", - resource: "events", - category: "events", - multiSelect: true, + columns: [ + { + name: "title", + label: "EVENTS.EVENTS.TABLE.TITLE", + sortable: true, + translate: false, + }, + { + template: "EventsPresentersCell", + name: "presenter", + label: "EVENTS.EVENTS.TABLE.PRESENTERS", + sortable: true, + translate: false, + }, + { + template: "EventsSeriesCell", + name: "series_name", + label: "EVENTS.EVENTS.TABLE.SERIES", + sortable: true, + translate: false, + }, + { + template: "EventsDateCell", + name: "date", + label: "EVENTS.EVENTS.TABLE.DATE", + sortable: true, + translate: false, + }, + { + template: "EventsStartCell", + name: "start_date", + label: "EVENTS.EVENTS.TABLE.START", + sortable: true, + translate: false, + }, + { + template: "EventsEndCell", + name: "end_date", + label: "EVENTS.EVENTS.TABLE.STOP", + sortable: true, + translate: false, + }, + { + template: "EventsLocationCell", + name: "location", + label: "EVENTS.EVENTS.TABLE.LOCATION", + sortable: true, + translate: false, + }, + { + name: "published", + label: "EVENTS.EVENTS.TABLE.PUBLISHED", + template: "PublishedCell", + translate: false, + }, + { + template: "EventsStatusCell", + name: "event_status", + label: "EVENTS.EVENTS.TABLE.STATUS", + sortable: true, + translate: true, + }, + { + name: "actions", + template: "EventActionsCell", + label: "EVENTS.EVENTS.TABLE.ACTION", + translate: false, + }, + { + name: "notes", + template: "EventsNotesCell", + label: "EVENTS.EVENTS.TABLE.ADMINUI_NOTES", + translate: false, + deactivated: true, + }, + ], + caption: "EVENTS.EVENTS.TABLE.CAPTION", + resource: "events", + category: "events", + multiSelect: true, }; diff --git a/src/configs/tableConfigs/eventsTableMap.ts b/src/configs/tableConfigs/eventsTableMap.ts index 26a709ce4d..cbcaa8ec9a 100644 --- a/src/configs/tableConfigs/eventsTableMap.ts +++ b/src/configs/tableConfigs/eventsTableMap.ts @@ -15,15 +15,15 @@ import EventsNotesCell from "../../components/events/partials/EventsNotesCell"; * This helps to render different templates of cells more dynamically */ export const eventsTemplateMap = { - EventActionsCell: EventActionCell, - EventsDateCell: EventsDateCell, - EventsStartCell: EventsStartCell, - EventsEndCell: EventsEndCell, - EventsLocationCell: EventsLocationCell, - EventsPresentersCell: EventsPresentersCell, - EventsSeriesCell: EventsSeriesCell, - EventsStatusCell: EventsStatusCell, - EventsTechnicalDateCell: EventsTechnicalDateCell, - PublishedCell: PublishedCell, - EventsNotesCell: EventsNotesCell, + EventActionsCell: EventActionCell, + EventsDateCell: EventsDateCell, + EventsStartCell: EventsStartCell, + EventsEndCell: EventsEndCell, + EventsLocationCell: EventsLocationCell, + EventsPresentersCell: EventsPresentersCell, + EventsSeriesCell: EventsSeriesCell, + EventsStatusCell: EventsStatusCell, + EventsTechnicalDateCell: EventsTechnicalDateCell, + PublishedCell: PublishedCell, + EventsNotesCell: EventsNotesCell, }; diff --git a/src/configs/tableConfigs/groupsTableConfig.ts b/src/configs/tableConfigs/groupsTableConfig.ts index 92e732a539..a843f133ca 100644 --- a/src/configs/tableConfigs/groupsTableConfig.ts +++ b/src/configs/tableConfigs/groupsTableConfig.ts @@ -11,30 +11,30 @@ import { TableConfig } from "./aclsTableConfig"; * - is multi select possible? */ export const groupsTableConfig: TableConfig = { - columns: [ - { - name: "name", - label: "USERS.GROUPS.TABLE.NAME", - sortable: true, - }, - { - name: "description", - label: "USERS.GROUPS.TABLE.DESCRIPTION", - sortable: true, - }, - { - name: "role", - label: "USERS.GROUPS.TABLE.ROLE", - sortable: true, - }, - { - template: "GroupsActionsCell", - name: "actions", - label: "USERS.USERS.TABLE.ACTION", - }, - ], - caption: "USERS.GROUPS.TABLE.CAPTION", - resource: "groups", - category: "users", - multiSelect: false, + columns: [ + { + name: "name", + label: "USERS.GROUPS.TABLE.NAME", + sortable: true, + }, + { + name: "description", + label: "USERS.GROUPS.TABLE.DESCRIPTION", + sortable: true, + }, + { + name: "role", + label: "USERS.GROUPS.TABLE.ROLE", + sortable: true, + }, + { + template: "GroupsActionsCell", + name: "actions", + label: "USERS.USERS.TABLE.ACTION", + }, + ], + caption: "USERS.GROUPS.TABLE.CAPTION", + resource: "groups", + category: "users", + multiSelect: false, }; diff --git a/src/configs/tableConfigs/groupsTableMap.ts b/src/configs/tableConfigs/groupsTableMap.ts index 449d81a71b..a4109ffd77 100644 --- a/src/configs/tableConfigs/groupsTableMap.ts +++ b/src/configs/tableConfigs/groupsTableMap.ts @@ -6,5 +6,5 @@ import GroupsActionsCell from "../../components/users/partials/GroupsActionsCell * uses template map. */ export const groupsTemplateMap = { - GroupsActionsCell: GroupsActionsCell, + GroupsActionsCell: GroupsActionsCell, }; diff --git a/src/configs/tableConfigs/jobsTableConfig.ts b/src/configs/tableConfigs/jobsTableConfig.ts index 0a793b6d89..5091ca6d71 100644 --- a/src/configs/tableConfigs/jobsTableConfig.ts +++ b/src/configs/tableConfigs/jobsTableConfig.ts @@ -13,60 +13,60 @@ import { TableConfig } from "./aclsTableConfig"; * - is multi select possible? */ export const jobsTableConfig: TableConfig = { - columns: [ - { - name: "id", - label: "SYSTEMS.JOBS.TABLE.ID", - sortable: true, - }, - { - name: "status", - label: "SYSTEMS.JOBS.TABLE.STATUS", - translate: true, - sortable: true, - }, - { - name: "operation", - label: "SYSTEMS.JOBS.TABLE.OPERATION", - sortable: true, - }, - { - name: "type", - label: "SYSTEMS.JOBS.TABLE.TYPE", - sortable: true, - }, - { - name: "processingHost", - label: "SYSTEMS.JOBS.TABLE.HOST_NAME", - sortable: true, - }, - { - name: "processingNode", - label: "SYSTEMS.JOBS.TABLE.NODE_NAME", - sortable: true, - }, - { - template: "JobsSubmittedCell", - name: "submitted", - label: "SYSTEMS.JOBS.TABLE.SUBMITTED", - sortable: true, - }, - { - template: "JobsStartedCell", - name: "started", - label: "SYSTEMS.JOBS.TABLE.STARTED", - sortable: true, - }, - { - name: "creator", - label: "SYSTEMS.JOBS.TABLE.CREATOR", - sortable: true, - }, - ], - caption: "SYSTEMS.JOBS.TABLE.CAPTION", - resource: "jobs", - category: "systems", - multiSelect: false, + columns: [ + { + name: "id", + label: "SYSTEMS.JOBS.TABLE.ID", + sortable: true, + }, + { + name: "status", + label: "SYSTEMS.JOBS.TABLE.STATUS", + translate: true, + sortable: true, + }, + { + name: "operation", + label: "SYSTEMS.JOBS.TABLE.OPERATION", + sortable: true, + }, + { + name: "type", + label: "SYSTEMS.JOBS.TABLE.TYPE", + sortable: true, + }, + { + name: "processingHost", + label: "SYSTEMS.JOBS.TABLE.HOST_NAME", + sortable: true, + }, + { + name: "processingNode", + label: "SYSTEMS.JOBS.TABLE.NODE_NAME", + sortable: true, + }, + { + template: "JobsSubmittedCell", + name: "submitted", + label: "SYSTEMS.JOBS.TABLE.SUBMITTED", + sortable: true, + }, + { + template: "JobsStartedCell", + name: "started", + label: "SYSTEMS.JOBS.TABLE.STARTED", + sortable: true, + }, + { + name: "creator", + label: "SYSTEMS.JOBS.TABLE.CREATOR", + sortable: true, + }, + ], + caption: "SYSTEMS.JOBS.TABLE.CAPTION", + resource: "jobs", + category: "systems", + multiSelect: false, }; /** @@ -75,6 +75,6 @@ export const jobsTableConfig: TableConfig = { * uses template map. */ export const jobsTemplateMap = { - JobsStartedCell: JobsStartedCell, - JobsSubmittedCell: JobsSubmittedCell, + JobsStartedCell: JobsStartedCell, + JobsSubmittedCell: JobsSubmittedCell, }; diff --git a/src/configs/tableConfigs/recordingsTableConfig.ts b/src/configs/tableConfigs/recordingsTableConfig.ts index 44a18b840c..556ed39469 100644 --- a/src/configs/tableConfigs/recordingsTableConfig.ts +++ b/src/configs/tableConfigs/recordingsTableConfig.ts @@ -11,34 +11,34 @@ import { TableConfig } from "./aclsTableConfig"; * - is multi select possible? */ export const recordingsTableConfig: TableConfig = { - columns: [ - { - name: "status", - template: "RecordingsStatusCell", - label: "RECORDINGS.RECORDINGS.TABLE.STATUS", - translate: true, - sortable: true, - }, - { - template: "RecordingsNameCell", - name: "name", - label: "RECORDINGS.RECORDINGS.TABLE.NAME", - sortable: true, - }, - { - template: "RecordingsUpdateCell", - name: "update", - label: "RECORDINGS.RECORDINGS.TABLE.UPDATED", - sortable: true, - }, - { - template: "RecordingsActionCell", - name: "actions", - label: "RECORDINGS.RECORDINGS.TABLE.ACTION", - }, - ], - caption: "RECORDINGS.RECORDINGS.TABLE.CAPTION", - resource: "recordings", - category: "recordings", - multiSelect: false, + columns: [ + { + name: "status", + template: "RecordingsStatusCell", + label: "RECORDINGS.RECORDINGS.TABLE.STATUS", + translate: true, + sortable: true, + }, + { + template: "RecordingsNameCell", + name: "name", + label: "RECORDINGS.RECORDINGS.TABLE.NAME", + sortable: true, + }, + { + template: "RecordingsUpdateCell", + name: "update", + label: "RECORDINGS.RECORDINGS.TABLE.UPDATED", + sortable: true, + }, + { + template: "RecordingsActionCell", + name: "actions", + label: "RECORDINGS.RECORDINGS.TABLE.ACTION", + }, + ], + caption: "RECORDINGS.RECORDINGS.TABLE.CAPTION", + resource: "recordings", + category: "recordings", + multiSelect: false, }; diff --git a/src/configs/tableConfigs/recordingsTableMap.ts b/src/configs/tableConfigs/recordingsTableMap.ts index 0393b4f4b0..8516946b2f 100644 --- a/src/configs/tableConfigs/recordingsTableMap.ts +++ b/src/configs/tableConfigs/recordingsTableMap.ts @@ -8,8 +8,8 @@ import RecordingsUpdateCell from "../../components/recordings/partials/Recording * This helps to render different templates of cells more dynamically */ export const recordingsTemplateMap = { - RecordingsActionCell: RecordingsActionCell, - RecordingsNameCell: RecordingsNameCell, - RecordingsStatusCell: RecordingsStatusCell, - RecordingsUpdateCell: RecordingsUpdateCell, + RecordingsActionCell: RecordingsActionCell, + RecordingsNameCell: RecordingsNameCell, + RecordingsStatusCell: RecordingsStatusCell, + RecordingsUpdateCell: RecordingsUpdateCell, }; diff --git a/src/configs/tableConfigs/seriesTableConfig.ts b/src/configs/tableConfigs/seriesTableConfig.ts index d2f004d25f..d492e48a9f 100644 --- a/src/configs/tableConfigs/seriesTableConfig.ts +++ b/src/configs/tableConfigs/seriesTableConfig.ts @@ -11,39 +11,39 @@ import { TableConfig } from "./aclsTableConfig"; * - is multi select possible? */ export const seriesTableConfig: TableConfig = { - columns: [ - { - template: "SeriesTitleCell", - name: "title", - label: "EVENTS.SERIES.TABLE.TITLE", - sortable: true, - }, - { - template: "SeriesCreatorsCell", - name: "organizers", - label: "EVENTS.SERIES.TABLE.ORGANIZERS", - sortable: true, - }, - { - template: "SeriesContributorsCell", - name: "contributors", - label: "EVENTS.SERIES.TABLE.CONTRIBUTORS", - sortable: true, - }, - { - template: "SeriesDateTimeCell", - name: "createdDateTime", - label: "EVENTS.SERIES.TABLE.CREATED", - sortable: true, - }, - { - template: "SeriesActionsCell", - name: "actions", - label: "EVENTS.SERIES.TABLE.ACTION", - }, - ], - caption: "EVENTS.SERIES.TABLE.CAPTION", - resource: "series", - category: "events", - multiSelect: true, + columns: [ + { + template: "SeriesTitleCell", + name: "title", + label: "EVENTS.SERIES.TABLE.TITLE", + sortable: true, + }, + { + template: "SeriesCreatorsCell", + name: "organizers", + label: "EVENTS.SERIES.TABLE.ORGANIZERS", + sortable: true, + }, + { + template: "SeriesContributorsCell", + name: "contributors", + label: "EVENTS.SERIES.TABLE.CONTRIBUTORS", + sortable: true, + }, + { + template: "SeriesDateTimeCell", + name: "createdDateTime", + label: "EVENTS.SERIES.TABLE.CREATED", + sortable: true, + }, + { + template: "SeriesActionsCell", + name: "actions", + label: "EVENTS.SERIES.TABLE.ACTION", + }, + ], + caption: "EVENTS.SERIES.TABLE.CAPTION", + resource: "series", + category: "events", + multiSelect: true, }; diff --git a/src/configs/tableConfigs/seriesTableMap.ts b/src/configs/tableConfigs/seriesTableMap.ts index 88a113035f..b686832137 100644 --- a/src/configs/tableConfigs/seriesTableMap.ts +++ b/src/configs/tableConfigs/seriesTableMap.ts @@ -9,9 +9,9 @@ import SeriesActionsCell from "../../components/events/partials/SeriesActionsCel * This helps to render different templates of cells more dynamically */ export const seriesTemplateMap = { - SeriesTitleCell: SeriesTitleCell, - SeriesCreatorsCell: SeriesCreatorsCell, - SeriesContributorsCell: SeriesContributorsCell, - SeriesDateTimeCell: SeriesDateTimeCell, - SeriesActionsCell: SeriesActionsCell, + SeriesTitleCell: SeriesTitleCell, + SeriesCreatorsCell: SeriesCreatorsCell, + SeriesContributorsCell: SeriesContributorsCell, + SeriesDateTimeCell: SeriesDateTimeCell, + SeriesActionsCell: SeriesActionsCell, }; diff --git a/src/configs/tableConfigs/serversTableConfig.ts b/src/configs/tableConfigs/serversTableConfig.ts index 380867d363..9512808b0e 100644 --- a/src/configs/tableConfigs/serversTableConfig.ts +++ b/src/configs/tableConfigs/serversTableConfig.ts @@ -11,52 +11,52 @@ import { TableConfig } from "./aclsTableConfig"; * - is multi select possible? */ export const serversTableConfig: TableConfig = { - columns: [ - { - template: "ServersStatusCell", - name: "online", - label: "SYSTEMS.SERVERS.TABLE.STATUS", - sortable: true, - }, - { - name: "hostname", - label: "SYSTEMS.SERVERS.TABLE.HOST_NAME", - sortable: true, - }, - { - name: "nodeName", - label: "SYSTEMS.SERVERS.TABLE.NODE_NAME", - sortable: true, - }, - { - name: "cores", - label: "SYSTEMS.SERVERS.TABLE.CORES", - sortable: true, - }, - { - name: "completed", - label: "SYSTEMS.SERVERS.TABLE.COMPLETED", - sortable: true, - }, - { - name: "running", - label: "SYSTEMS.SERVERS.TABLE.RUNNING", - sortable: true, - }, - { - name: "queued", - label: "SYSTEMS.SERVERS.TABLE.QUEUED", - sortable: true, - }, - { - template: "ServersMaintenanceCell", - name: "maintenance", - label: "SYSTEMS.SERVERS.TABLE.MAINTENANCE", - sortable: true, - }, - ], - caption: "SYSTEMS.SERVERS.TABLE.CAPTION", - resource: "servers", - category: "systems", - multiSelect: false, + columns: [ + { + template: "ServersStatusCell", + name: "online", + label: "SYSTEMS.SERVERS.TABLE.STATUS", + sortable: true, + }, + { + name: "hostname", + label: "SYSTEMS.SERVERS.TABLE.HOST_NAME", + sortable: true, + }, + { + name: "nodeName", + label: "SYSTEMS.SERVERS.TABLE.NODE_NAME", + sortable: true, + }, + { + name: "cores", + label: "SYSTEMS.SERVERS.TABLE.CORES", + sortable: true, + }, + { + name: "completed", + label: "SYSTEMS.SERVERS.TABLE.COMPLETED", + sortable: true, + }, + { + name: "running", + label: "SYSTEMS.SERVERS.TABLE.RUNNING", + sortable: true, + }, + { + name: "queued", + label: "SYSTEMS.SERVERS.TABLE.QUEUED", + sortable: true, + }, + { + template: "ServersMaintenanceCell", + name: "maintenance", + label: "SYSTEMS.SERVERS.TABLE.MAINTENANCE", + sortable: true, + }, + ], + caption: "SYSTEMS.SERVERS.TABLE.CAPTION", + resource: "servers", + category: "systems", + multiSelect: false, }; diff --git a/src/configs/tableConfigs/serversTableMap.ts b/src/configs/tableConfigs/serversTableMap.ts index 410064bc13..a5a68839a8 100644 --- a/src/configs/tableConfigs/serversTableMap.ts +++ b/src/configs/tableConfigs/serversTableMap.ts @@ -7,6 +7,6 @@ import ServersMaintenanceCell from "../../components/systems/partials/ServersMai * uses template map. */ export const serversTemplateMap = { - ServersStatusCell: ServersStatusCell, - ServersMaintenanceCell: ServersMaintenanceCell, + ServersStatusCell: ServersStatusCell, + ServersMaintenanceCell: ServersMaintenanceCell, }; diff --git a/src/configs/tableConfigs/servicesTableConfig.ts b/src/configs/tableConfigs/servicesTableConfig.ts index 64ff46e5bf..5f4cd5d990 100644 --- a/src/configs/tableConfigs/servicesTableConfig.ts +++ b/src/configs/tableConfigs/servicesTableConfig.ts @@ -11,63 +11,63 @@ import { TableConfig } from "./aclsTableConfig"; * - is multi select possible? */ export const servicesTableConfig: TableConfig = { - columns: [ - { - name: "status", - label: "SYSTEMS.SERVICES.TABLE.STATUS", - translate: true, - sortable: true, - }, - { - name: "name", - label: "SYSTEMS.SERVICES.TABLE.NAME", - sortable: true, - }, - { - name: "hostname", - label: "SYSTEMS.SERVICES.TABLE.HOST_NAME", - sortable: true, - }, - { - name: "nodeName", - label: "SYSTEMS.SERVICES.TABLE.NODE_NAME", - sortable: true, - }, - { - name: "completed", - label: "SYSTEMS.SERVICES.TABLE.COMPLETED", - sortable: true, - }, - { - name: "running", - label: "SYSTEMS.SERVICES.TABLE.RUNNING", - sortable: true, - }, - { - name: "queued", - label: "SYSTEMS.SERVICES.TABLE.QUEUED", - sortable: true, - }, - { - template: "MeanRunTimeCell", - name: "meanRunTime", - label: "SYSTEMS.SERVICES.TABLE.MEAN_RUN_TIME", - sortable: true, - }, - { - template: "MeanQueueTimeCell", - name: "meanQueueTime", - label: "SYSTEMS.SERVICES.TABLE.MEAN_QUEUE_TIME", - sortable: true, - }, - { - template: "ServicesActionsCell", - name: "actions", - label: "SYSTEMS.SERVICES.TABLE.ACTION", - }, - ], - caption: "SYSTEMS.SERVICES.TABLE.CAPTION", - resource: "services", - category: "systems", - multiSelect: false, + columns: [ + { + name: "status", + label: "SYSTEMS.SERVICES.TABLE.STATUS", + translate: true, + sortable: true, + }, + { + name: "name", + label: "SYSTEMS.SERVICES.TABLE.NAME", + sortable: true, + }, + { + name: "hostname", + label: "SYSTEMS.SERVICES.TABLE.HOST_NAME", + sortable: true, + }, + { + name: "nodeName", + label: "SYSTEMS.SERVICES.TABLE.NODE_NAME", + sortable: true, + }, + { + name: "completed", + label: "SYSTEMS.SERVICES.TABLE.COMPLETED", + sortable: true, + }, + { + name: "running", + label: "SYSTEMS.SERVICES.TABLE.RUNNING", + sortable: true, + }, + { + name: "queued", + label: "SYSTEMS.SERVICES.TABLE.QUEUED", + sortable: true, + }, + { + template: "MeanRunTimeCell", + name: "meanRunTime", + label: "SYSTEMS.SERVICES.TABLE.MEAN_RUN_TIME", + sortable: true, + }, + { + template: "MeanQueueTimeCell", + name: "meanQueueTime", + label: "SYSTEMS.SERVICES.TABLE.MEAN_QUEUE_TIME", + sortable: true, + }, + { + template: "ServicesActionsCell", + name: "actions", + label: "SYSTEMS.SERVICES.TABLE.ACTION", + }, + ], + caption: "SYSTEMS.SERVICES.TABLE.CAPTION", + resource: "services", + category: "systems", + multiSelect: false, }; diff --git a/src/configs/tableConfigs/servicesTableMap.ts b/src/configs/tableConfigs/servicesTableMap.ts index e0c0ca4726..d8e28bec76 100644 --- a/src/configs/tableConfigs/servicesTableMap.ts +++ b/src/configs/tableConfigs/servicesTableMap.ts @@ -8,7 +8,7 @@ import ServicesActionCell from "../../components/systems/partials/ServicesAction * uses template map. */ export const servicesTemplateMap = { - MeanRunTimeCell: MeanRunTimeCell, - MeanQueueTimeCell: MeanQueueTimeCell, - ServicesActionsCell: ServicesActionCell, + MeanRunTimeCell: MeanRunTimeCell, + MeanQueueTimeCell: MeanQueueTimeCell, + ServicesActionsCell: ServicesActionCell, }; diff --git a/src/configs/tableConfigs/themesTableConfig.ts b/src/configs/tableConfigs/themesTableConfig.ts index 7c826a2cdb..bd4246c51e 100644 --- a/src/configs/tableConfigs/themesTableConfig.ts +++ b/src/configs/tableConfigs/themesTableConfig.ts @@ -11,35 +11,35 @@ import { TableConfig } from "./aclsTableConfig"; * - is multi select possible? */ export const themesTableConfig: TableConfig = { - columns: [ - { - name: "name", - label: "CONFIGURATION.THEMES.TABLE.NAME", - sortable: true, - }, - { - name: "description", - label: "CONFIGURATION.THEMES.TABLE.DESCRIPTION", - sortable: true, - }, - { - name: "creator", - label: "CONFIGURATION.THEMES.TABLE.CREATOR", - sortable: true, - }, - { - name: "creation_date", - label: "CONFIGURATION.THEMES.TABLE.CREATED", - sortable: true, - }, - { - template: "ThemesActionsCell", - name: "actions", - label: "CONFIGURATION.THEMES.TABLE.ACTION", - }, - ], - caption: "CONFIGURATION.THEMES.TABLE.CAPTION", - resource: "themes", - category: "configuration", - multiSelect: false, + columns: [ + { + name: "name", + label: "CONFIGURATION.THEMES.TABLE.NAME", + sortable: true, + }, + { + name: "description", + label: "CONFIGURATION.THEMES.TABLE.DESCRIPTION", + sortable: true, + }, + { + name: "creator", + label: "CONFIGURATION.THEMES.TABLE.CREATOR", + sortable: true, + }, + { + name: "creation_date", + label: "CONFIGURATION.THEMES.TABLE.CREATED", + sortable: true, + }, + { + template: "ThemesActionsCell", + name: "actions", + label: "CONFIGURATION.THEMES.TABLE.ACTION", + }, + ], + caption: "CONFIGURATION.THEMES.TABLE.CAPTION", + resource: "themes", + category: "configuration", + multiSelect: false, }; diff --git a/src/configs/tableConfigs/themesTableMap.ts b/src/configs/tableConfigs/themesTableMap.ts index a4ea0e2190..b4b9bea3e9 100644 --- a/src/configs/tableConfigs/themesTableMap.ts +++ b/src/configs/tableConfigs/themesTableMap.ts @@ -6,5 +6,5 @@ import ThemesActionsCell from "../../components/configuration/partials/ThemesAct * uses template map. */ export const themesTemplateMap = { - ThemesActionsCell: ThemesActionsCell, + ThemesActionsCell: ThemesActionsCell, }; diff --git a/src/configs/tableConfigs/usersTableConfig.ts b/src/configs/tableConfigs/usersTableConfig.ts index d858524c74..b6c464a4ee 100644 --- a/src/configs/tableConfigs/usersTableConfig.ts +++ b/src/configs/tableConfigs/usersTableConfig.ts @@ -11,42 +11,42 @@ import { TableConfig } from "./aclsTableConfig"; * - is multi select possible? */ export const usersTableConfig: TableConfig = { - columns: [ - { - name: "name", - label: "USERS.USERS.TABLE.NAME", - sortable: true, - }, - { - name: "username", - label: "USERS.USERS.TABLE.USERNAME", - sortable: true, - }, - { - name: "email", - label: "USERS.USERS.TABLE.EMAIL", - sortable: true, - }, - { - template: "UsersRolesCell", - name: "roles", - label: "USERS.USERS.TABLE.ROLES", - sortable: true, - }, - { - name: "provider", - label: "USERS.USERS.TABLE.PROVIDER", - sortable: true, - }, - { - template: "UsersActionsCell", - name: "actions", - label: "USERS.USERS.TABLE.ACTION", - }, - ], - caption: "USERS.USERS.TABLE.CAPTION", - resource: "users", - category: "users", - multiSelect: false, + columns: [ + { + name: "name", + label: "USERS.USERS.TABLE.NAME", + sortable: true, + }, + { + name: "username", + label: "USERS.USERS.TABLE.USERNAME", + sortable: true, + }, + { + name: "email", + label: "USERS.USERS.TABLE.EMAIL", + sortable: true, + }, + { + template: "UsersRolesCell", + name: "roles", + label: "USERS.USERS.TABLE.ROLES", + sortable: true, + }, + { + name: "provider", + label: "USERS.USERS.TABLE.PROVIDER", + sortable: true, + }, + { + template: "UsersActionsCell", + name: "actions", + label: "USERS.USERS.TABLE.ACTION", + }, + ], + caption: "USERS.USERS.TABLE.CAPTION", + resource: "users", + category: "users", + multiSelect: false, }; diff --git a/src/configs/tableConfigs/usersTableMap.ts b/src/configs/tableConfigs/usersTableMap.ts index 779668b582..fce4ed5b55 100644 --- a/src/configs/tableConfigs/usersTableMap.ts +++ b/src/configs/tableConfigs/usersTableMap.ts @@ -7,6 +7,6 @@ import UsersRolesCell from "../../components/users/partials/UsersRolesCell"; * uses template map. */ export const usersTemplateMap = { - UsersActionsCell: UsersActionCell, - UsersRolesCell: UsersRolesCell, + UsersActionsCell: UsersActionCell, + UsersRolesCell: UsersRolesCell, }; diff --git a/src/hooks/wizardHooks.ts b/src/hooks/wizardHooks.ts index 92bc3d20ac..f54a7e71a5 100644 --- a/src/hooks/wizardHooks.ts +++ b/src/hooks/wizardHooks.ts @@ -4,128 +4,128 @@ import { Event } from "../slices/eventSlice"; import { isEvent } from "../slices/tableSlice"; export const usePageFunctions = (initialPage: number, initialValues: initialValuesType) => { - const [page, setPage] = useState(initialPage); - const [snapshot, setSnapshot] = useState(initialValues); - const [pageCompleted, setPageCompleted] = useState<{ [key: number]: boolean }>({}); - - const nextPage = (values: initialValuesType) => { - setSnapshot(values); - - // set page as completely filled out - let updatedPageCompleted = pageCompleted; - updatedPageCompleted[page] = true; - setPageCompleted(updatedPageCompleted); - - setPage(page + 1); - }; - - const previousPage = (values: initialValuesType) => { - setSnapshot(values); - setPage(page - 1); - }; - - return { - snapshot, - page, - nextPage, - previousPage, - setPage, - pageCompleted, - setPageCompleted, - }; + const [page, setPage] = useState(initialPage); + const [snapshot, setSnapshot] = useState(initialValues); + const [pageCompleted, setPageCompleted] = useState<{ [key: number]: boolean }>({}); + + const nextPage = (values: initialValuesType) => { + setSnapshot(values); + + // set page as completely filled out + let updatedPageCompleted = pageCompleted; + updatedPageCompleted[page] = true; + setPageCompleted(updatedPageCompleted); + + setPage(page + 1); + }; + + const previousPage = (values: initialValuesType) => { + setSnapshot(values); + setPage(page - 1); + }; + + return { + snapshot, + page, + nextPage, + previousPage, + setPage, + pageCompleted, + setPageCompleted, + }; }; interface RequiredFormProps { - events: Event[], + events: Event[], } export const useSelectionChanges = ( - formik: FormikProps, - selectedRows: Event[] + formik: FormikProps, + selectedRows: Event[] ) => { - const [selectedEvents, setSelectedEvents] = useState( - formik.values.events.length === 0 ? selectedRows : formik.values.events - ); - const [allChecked, setAllChecked] = useState( - formik.values.events.length === 0 - ? true - : formik.values.events.every((event) => event.selected === true) - ); - - // Select or deselect all rows in table - const onChangeAllSelected = (e: React.ChangeEvent) => { - const selected = e.target.checked; - setAllChecked(selected); - let changedSelection = selectedEvents.map((event) => { - return { - ...event, - selected: selected, - }; - }); - setSelectedEvents(changedSelection); - formik.setFieldValue("events", changedSelection); - }; - - // Handle change of checkboxes indicating which events to consider further - const onChangeSelected = (e: React.ChangeEvent, id: string) => { - const selected = e.target.checked; - let changedEvents = selectedEvents.map((event) => { - if (isEvent(event) && event.id === id) { - return { - ...event, - selected: selected, - }; - } else { - return event; - } - }); - setSelectedEvents(changedEvents); - formik.setFieldValue("events", changedEvents); - - if (!selected) { - setAllChecked(false); - } - if (changedEvents.every((event) => event.selected === true)) { - setAllChecked(true); - } - }; - - return { - selectedEvents, - allChecked, - onChangeSelected, - onChangeAllSelected - }; + const [selectedEvents, setSelectedEvents] = useState( + formik.values.events.length === 0 ? selectedRows : formik.values.events + ); + const [allChecked, setAllChecked] = useState( + formik.values.events.length === 0 + ? true + : formik.values.events.every((event) => event.selected === true) + ); + + // Select or deselect all rows in table + const onChangeAllSelected = (e: React.ChangeEvent) => { + const selected = e.target.checked; + setAllChecked(selected); + let changedSelection = selectedEvents.map((event) => { + return { + ...event, + selected: selected, + }; + }); + setSelectedEvents(changedSelection); + formik.setFieldValue("events", changedSelection); + }; + + // Handle change of checkboxes indicating which events to consider further + const onChangeSelected = (e: React.ChangeEvent, id: string) => { + const selected = e.target.checked; + let changedEvents = selectedEvents.map((event) => { + if (isEvent(event) && event.id === id) { + return { + ...event, + selected: selected, + }; + } else { + return event; + } + }); + setSelectedEvents(changedEvents); + formik.setFieldValue("events", changedEvents); + + if (!selected) { + setAllChecked(false); + } + if (changedEvents.every((event) => event.selected === true)) { + setAllChecked(true); + } + }; + + return { + selectedEvents, + allChecked, + onChangeSelected, + onChangeAllSelected + }; }; export const useClickOutsideField = ( - childRef: React.RefObject, - isFirstField?: boolean, + childRef: React.RefObject, + isFirstField?: boolean, ) => { - // Indicator if currently edit mode is activated - const [editMode, setEditMode] = useState(isFirstField); - - useEffect(() => { - // Handle click outside the field and leave edit mode - const handleClickOutside = (e: MouseEvent) => { - if (childRef.current && !childRef.current.contains(e.target as Node)) { - setEditMode(false); - } - }; - - // Focus current field - if (childRef && childRef.current && editMode === true) { - childRef.current.focus(); - } - - // Adding event listener for detecting click outside - window.addEventListener("mousedown", handleClickOutside); - - return () => { - window.removeEventListener("mousedown", handleClickOutside); - }; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [editMode]); - - return {editMode, setEditMode}; + // Indicator if currently edit mode is activated + const [editMode, setEditMode] = useState(isFirstField); + + useEffect(() => { + // Handle click outside the field and leave edit mode + const handleClickOutside = (e: MouseEvent) => { + if (childRef.current && !childRef.current.contains(e.target as Node)) { + setEditMode(false); + } + }; + + // Focus current field + if (childRef && childRef.current && editMode === true) { + childRef.current.focus(); + } + + // Adding event listener for detecting click outside + window.addEventListener("mousedown", handleClickOutside); + + return () => { + window.removeEventListener("mousedown", handleClickOutside); + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [editMode]); + + return {editMode, setEditMode}; }; diff --git a/src/i18n/i18n.ts b/src/i18n/i18n.ts index 4a6c90797b..2a791013bc 100644 --- a/src/i18n/i18n.ts +++ b/src/i18n/i18n.ts @@ -27,48 +27,48 @@ import zhTWTrans from "./org/opencastproject/adminui/languages/lang-zh_TW.json"; // Assignment of language code to translation file // !!! If translation file of a new language is added, please add assignment here, too !!! const resources = { - "en-US": { translation: enUSTrans }, - "en-GB": { translation: enGBTrans }, - da: { translation: daDKTrans }, - de: { translation: deDETrans }, - el: { translation: elGRTrans }, - es: { translation: esESTrans }, - fr: { translation: frFRTrans }, - gl: { translation: glESTrans }, - he: { translation: heILTrans }, - it: { translation: itITTrans }, - nl: { translation: nlNLTrans }, - pl: { translation: plPLTrans }, - sl: { translation: slSITrans }, - sv: { translation: svSETrans }, - tr: { translation: trTRTrans }, - "zh-CN": { translation: zhCNTrans }, - "zh-TW": { translation: zhTWTrans }, + "en-US": { translation: enUSTrans }, + "en-GB": { translation: enGBTrans }, + da: { translation: daDKTrans }, + de: { translation: deDETrans }, + el: { translation: elGRTrans }, + es: { translation: esESTrans }, + fr: { translation: frFRTrans }, + gl: { translation: glESTrans }, + he: { translation: heILTrans }, + it: { translation: itITTrans }, + nl: { translation: nlNLTrans }, + pl: { translation: plPLTrans }, + sl: { translation: slSITrans }, + sv: { translation: svSETrans }, + tr: { translation: trTRTrans }, + "zh-CN": { translation: zhCNTrans }, + "zh-TW": { translation: zhTWTrans }, } as const; // Configuration of i18next i18n - .use(HttpBackend) - .use(LanguageDetector) - .use(initReactI18next) - .init({ - resources, - fallbackLng: "en-US", - debug: true, + .use(HttpBackend) + .use(LanguageDetector) + .use(initReactI18next) + .init({ + resources, + fallbackLng: "en-US", + debug: true, - interpolation: { - escapeValue: false, - format: function (value, format, lng) { - if (value instanceof Date) { - return moment(value).format(format); - } + interpolation: { + escapeValue: false, + format: function (value, format, lng) { + if (value instanceof Date) { + return moment(value).format(format); + } - return value; - }, - }, - react: { - useSuspense: false, - }, - }); + return value; + }, + }, + react: { + useSuspense: false, + }, + }); export default i18n; diff --git a/src/i18n/languages.ts b/src/i18n/languages.ts index 58f069eaf6..5462d0f58c 100644 --- a/src/i18n/languages.ts +++ b/src/i18n/languages.ts @@ -27,23 +27,23 @@ import {zhTW} from "date-fns/locale/zh-TW"; * !!! If a translation file of a new language was added, please insert these language here, too !!! */ const languages = [ - { code: "en-US", long: "English (US)", rtl: false, dateLocale: enUS }, - { code: "en-GB", long: "English (UK)", rtl: false, dateLocale: enGB }, - { code: "da", long: "Dansk", rtl: false, dateLocale: da }, - { code: "de", long: "Deutsch", rtl: false, dateLocale: de }, - { code: "el", long: "Ελληνικά", rtl: false, dateLocale: el }, - { code: "es", long: "Español", rtl: false, dateLocale: es }, - { code: "fr", long: "Français", rtl: false, dateLocale: fr }, - { code: "gl", long: "Galego", rtl: false, dateLocale: gl }, - { code: "he", long: "עברית", rtl: true, dateLocale: he }, - { code: "it", long: "Italiano", rtl: false, dateLocale: it }, - { code: "nl", long: "Nederlands", rtl: false, dateLocale: nl }, - { code: "pl", long: "Polski", rtl: false, dateLocale: pl }, - { code: "sl", long: "Slovenščina", rtl: false, dateLocale: sl }, - { code: "sv", long: "Svenska", rtl: false, dateLocale: sv }, - { code: "tr", long: "Türkçe", rtl: false, dateLocale: tr }, - { code: "zh-CN", long: "简体中文", rtl: false, dateLocale: zhCN }, - { code: "zh-TW", long: "繁體中文", rtl: false, dateLocale: zhTW }, + { code: "en-US", long: "English (US)", rtl: false, dateLocale: enUS }, + { code: "en-GB", long: "English (UK)", rtl: false, dateLocale: enGB }, + { code: "da", long: "Dansk", rtl: false, dateLocale: da }, + { code: "de", long: "Deutsch", rtl: false, dateLocale: de }, + { code: "el", long: "Ελληνικά", rtl: false, dateLocale: el }, + { code: "es", long: "Español", rtl: false, dateLocale: es }, + { code: "fr", long: "Français", rtl: false, dateLocale: fr }, + { code: "gl", long: "Galego", rtl: false, dateLocale: gl }, + { code: "he", long: "עברית", rtl: true, dateLocale: he }, + { code: "it", long: "Italiano", rtl: false, dateLocale: it }, + { code: "nl", long: "Nederlands", rtl: false, dateLocale: nl }, + { code: "pl", long: "Polski", rtl: false, dateLocale: pl }, + { code: "sl", long: "Slovenščina", rtl: false, dateLocale: sl }, + { code: "sv", long: "Svenska", rtl: false, dateLocale: sv }, + { code: "tr", long: "Türkçe", rtl: false, dateLocale: tr }, + { code: "zh-CN", long: "简体中文", rtl: false, dateLocale: zhCN }, + { code: "zh-TW", long: "繁體中文", rtl: false, dateLocale: zhTW }, ]; export default languages; diff --git a/src/index.tsx b/src/index.tsx index 4b9c169e95..2a9e859a3e 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -26,29 +26,29 @@ import { getCurrentLanguageInformation } from "./utils/utils"; const persistor = persistStore(store); const theme = createTheme({ - zIndex: { - modal: 2147483550, - } + zIndex: { + modal: 2147483550, + } }) const container = document.getElementById("root"); if (!container) { - throw new Error("Failed to find the root element"); + throw new Error("Failed to find the root element"); } const root = ReactDOMClient.createRoot(container); root.render( - - - loading...} persistor={persistor}> - - {/*locale={getCurrentLanguageInformation()?.dateLocale}> */} - - - - - - - - , + + + loading...} persistor={persistor}> + + {/*locale={getCurrentLanguageInformation()?.dateLocale}> */} + + + + + + + + , ); diff --git a/src/selectors/eventDetailsSelectors.ts b/src/selectors/eventDetailsSelectors.ts index a2d564621e..647f6ff2a9 100644 --- a/src/selectors/eventDetailsSelectors.ts +++ b/src/selectors/eventDetailsSelectors.ts @@ -6,54 +6,54 @@ export const getModalPage = (state: RootState) => state.eventDetails.modal.page; export const getModalEvent = (state: RootState) => state.eventDetails.modal.event; export const getModalWorkflowId = (state: RootState) => state.eventDetails.modal.workflowId; export const getModalWorkflowTabHierarchy = (state: RootState) => - state.eventDetails.modal.workflowTabHierarchy; + state.eventDetails.modal.workflowTabHierarchy; export const getModalAssetsTabHierarchy = (state: RootState) => - state.eventDetails.modal.assetsTabHierarchy; + state.eventDetails.modal.assetsTabHierarchy; /* selectors for metadata */ export const getMetadata = (state: RootState) => state.eventDetails.metadata; export const getExtendedMetadata = (state: RootState) => state.eventDetails.extendedMetadata; export const isFetchingMetadata = (state: RootState) => - state.eventDetails.statusMetadata === 'loading'; + state.eventDetails.statusMetadata === 'loading'; /* selectors for assets */ export const getAssets = (state: RootState) => state.eventDetails.assets; export const isFetchingAssets = (state: RootState) => - state.eventDetails.statusAssets === 'loading'; + state.eventDetails.statusAssets === 'loading'; export const isFetchingAssetAttachments = (state: RootState) => - state.eventDetails.statusAssetAttachments === 'loading'; + state.eventDetails.statusAssetAttachments === 'loading'; export const isFetchingAssetAttachmentDetails = (state: RootState) => - state.eventDetails.statusAssetAttachmentDetails === 'loading'; + state.eventDetails.statusAssetAttachmentDetails === 'loading'; export const isFetchingAssetCatalogs = (state: RootState) => - state.eventDetails.statusAssetCatalogs === 'loading'; + state.eventDetails.statusAssetCatalogs === 'loading'; export const isFetchingAssetCatalogDetails = (state: RootState) => - state.eventDetails.statusAssetCatalogDetails === 'loading'; + state.eventDetails.statusAssetCatalogDetails === 'loading'; export const isFetchingAssetMedia = (state: RootState) => - state.eventDetails.statusAssetMedia === 'loading'; + state.eventDetails.statusAssetMedia === 'loading'; export const isFetchingAssetMediaDetails = (state: RootState) => - state.eventDetails.statusAssetMediaDetails === 'loading'; + state.eventDetails.statusAssetMediaDetails === 'loading'; export const isFetchingAssetPublications = (state: RootState) => - state.eventDetails.statusAssetPublications === 'loading'; + state.eventDetails.statusAssetPublications === 'loading'; export const isFetchingAssetPublicationDetails = (state: RootState) => - state.eventDetails.statusAssetPublicationDetails === 'loading'; + state.eventDetails.statusAssetPublicationDetails === 'loading'; export const isTransactionReadOnly = (state: RootState) => - state.eventDetails.transactionsReadOnly; + state.eventDetails.transactionsReadOnly; export const getUploadAssetOptions = (state: RootState) => - state.eventDetails.uploadAssetOptions; + state.eventDetails.uploadAssetOptions; export const getAssetAttachments = (state: RootState) => - state.eventDetails.assetAttachments; + state.eventDetails.assetAttachments; export const getAssetAttachmentDetails = (state: RootState) => - state.eventDetails.assetAttachmentDetails; + state.eventDetails.assetAttachmentDetails; export const getAssetCatalogs = (state: RootState) => state.eventDetails.assetCatalogs; export const getAssetCatalogDetails = (state: RootState) => - state.eventDetails.assetCatalogDetails; + state.eventDetails.assetCatalogDetails; export const getAssetMedia = (state: RootState) => state.eventDetails.assetMedia; export const getAssetMediaDetails = (state: RootState) => - state.eventDetails.assetMediaDetails; + state.eventDetails.assetMediaDetails; export const getAssetPublications = (state: RootState) => - state.eventDetails.assetPublications; + state.eventDetails.assetPublications; export const getAssetPublicationDetails = (state: RootState) => - state.eventDetails.assetPublicationDetails; + state.eventDetails.assetPublicationDetails; /* selectors for policies */ export const getPolicies = (state: RootState) => state.eventDetails.policies; @@ -62,73 +62,73 @@ export const getPolicies = (state: RootState) => state.eventDetails.policies; export const getComments = (state: RootState) => state.eventDetails.comments; export const getCommentReasons = (state: RootState) => state.eventDetails.commentReasons; export const isFetchingComments = (state: RootState) => - state.eventDetails.statusComments === 'loading'; + state.eventDetails.statusComments === 'loading'; export const isSavingComment = (state: RootState) => - state.eventDetails.statusSaveComment === 'loading'; + state.eventDetails.statusSaveComment === 'loading'; export const isSavingCommentReply = (state: RootState) => - state.eventDetails.statusSaveCommentReply === 'loading'; + state.eventDetails.statusSaveCommentReply === 'loading'; /* selectors for scheduling */ export const getSchedulingProperties = (state: RootState) => - state.eventDetails.scheduling.hasProperties; + state.eventDetails.scheduling.hasProperties; export const isFetchingScheduling = (state: RootState) => - state.eventDetails.statusScheduling === 'loading'; + state.eventDetails.statusScheduling === 'loading'; export const isSavingScheduling = (state: RootState) => - state.eventDetails.statusSaveScheduling === 'loading'; + state.eventDetails.statusSaveScheduling === 'loading'; export const getSchedulingSource = (state: RootState) => - state.eventDetails.schedulingSource; + state.eventDetails.schedulingSource; export const getSchedulingConflicts = (state: RootState) => - state.eventDetails.schedulingConflicts; + state.eventDetails.schedulingConflicts; export const isCheckingConflicts = (state: RootState) => - state.eventDetails.statusCheckConflicts === 'loading'; + state.eventDetails.statusCheckConflicts === 'loading'; /* selectors for workflows */ export const getWorkflows = (state: RootState) => state.eventDetails.workflows; export const isFetchingWorkflows = (state: RootState) => - state.eventDetails.statusWorkflows === 'loading'; + state.eventDetails.statusWorkflows === 'loading'; export const getWorkflowDefinitions = (state: RootState) => - state.eventDetails.workflowDefinitions; + state.eventDetails.workflowDefinitions; export const getWorkflowConfiguration = (state: RootState) => - state.eventDetails.workflowConfiguration; + state.eventDetails.workflowConfiguration; export const getWorkflow = (state: RootState) => state.eventDetails.workflows.workflow; export const isFetchingWorkflowDetails = (state: RootState) => - state.eventDetails.statusWorkflowDetails === 'loading'; + state.eventDetails.statusWorkflowDetails === 'loading'; export const getBaseWorkflow = (state: RootState) => state.eventDetails.baseWorkflow; export const performingWorkflowAction = (state: RootState) => - state.eventDetails.statusDoWorkflowAction === 'loading'; + state.eventDetails.statusDoWorkflowAction === 'loading'; export const deletingWorkflow = (state: RootState) => - state.eventDetails.statusDeleteWorkflow === 'loading'; + state.eventDetails.statusDeleteWorkflow === 'loading'; export const getWorkflowOperations = (state: RootState) => - state.eventDetails.workflowOperations; + state.eventDetails.workflowOperations; export const isFetchingWorkflowOperations = (state: RootState) => - state.eventDetails.statusWorkflowOperations === 'loading'; + state.eventDetails.statusWorkflowOperations === 'loading'; export const getWorkflowOperationDetails = (state: RootState) => - state.eventDetails.workflowOperationDetails; + state.eventDetails.workflowOperationDetails; export const isFetchingWorkflowOperationDetails = (state: RootState) => - state.eventDetails.statusWorkflowOperationDetails === 'loading'; + state.eventDetails.statusWorkflowOperationDetails === 'loading'; export const getWorkflowErrors = (state: RootState) => state.eventDetails.workflowErrors; export const isFetchingWorkflowErrors = (state: RootState) => - state.eventDetails.statusWorkflowErrors === 'loading'; + state.eventDetails.statusWorkflowErrors === 'loading'; export const getWorkflowErrorDetails = (state: RootState) => - state.eventDetails.workflowErrorDetails; + state.eventDetails.workflowErrorDetails; export const isFetchingWorkflowErrorDetails = (state: RootState) => - state.eventDetails.statusWorkflowErrorDetails === 'loading'; + state.eventDetails.statusWorkflowErrorDetails === 'loading'; export const getEventDetailsTobiraData = (state: RootState) => - state.eventDetails.tobiraData; + state.eventDetails.tobiraData; export const getEventDetailsTobiraDataError = (state: RootState) => - state.eventDetails.errorTobiraData; + state.eventDetails.errorTobiraData; export const getEventDetailsTobiraStatus = (state: RootState) => - state.eventDetails.statusTobiraData; + state.eventDetails.statusTobiraData; /* selectors for publications */ export const getPublications = (state: RootState) => state.eventDetails.publications; /* selectors for statistics */ export const hasStatistics = (state: RootState) => - state.eventDetails.statistics.length > 0; + state.eventDetails.statistics.length > 0; export const getStatistics = (state: RootState) => state.eventDetails.statistics; export const hasStatisticsError = (state: RootState) => - state.eventDetails.hasStatisticsError; + state.eventDetails.hasStatisticsError; export const isFetchingStatistics = (state: RootState) => - state.eventDetails.statusStatistics === 'loading'; + state.eventDetails.statusStatistics === 'loading'; diff --git a/src/selectors/eventSelectors.ts b/src/selectors/eventSelectors.ts index 95909386c8..4c6abfbb3c 100644 --- a/src/selectors/eventSelectors.ts +++ b/src/selectors/eventSelectors.ts @@ -10,14 +10,14 @@ export const isLoading = (state: RootState) => state.events.status === 'loading' export const getEventMetadata = (state: RootState) => state.events.metadata; export const getExtendedEventMetadata = (state: RootState) => state.events.extendedMetadata; export const isLoadingScheduling = (state: RootState) => - state.events.statusSchedulingInfo === 'loading' + state.events.statusSchedulingInfo === 'loading' export const getSchedulingEditedEvents = (state: RootState) => - state.events.schedulingInfo.editedEvents; + state.events.schedulingInfo.editedEvents; export const getSchedulingSeriesOptions = (state: RootState) => - state.events.schedulingInfo.seriesOptions; + state.events.schedulingInfo.seriesOptions; export const getTotalEvents = (state: RootState) => state.events.total; export const getAssetUploadOptions = (state: RootState) => state.events.uploadAssetOptions; export const isFetchingAssetUploadOptions = (state: RootState) => - state.events.isFetchingAssetUploadOptions; + state.events.isFetchingAssetUploadOptions; export const getAssetUploadWorkflow = (state: RootState) => - state.events.uploadAssetWorkflow; + state.events.uploadAssetWorkflow; diff --git a/src/selectors/notificationSelector.ts b/src/selectors/notificationSelector.ts index ee6b7b1ffe..63bfe7e01b 100644 --- a/src/selectors/notificationSelector.ts +++ b/src/selectors/notificationSelector.ts @@ -8,14 +8,14 @@ export const getGlobalPositions = (state: RootState) => export const getNotificationById = (id: OurNotification["id"]) => - createSelector(getNotifications, (notifications) => - notifications.filter((notification) => notification.id === id) - ); + createSelector(getNotifications, (notifications) => + notifications.filter((notification) => notification.id === id) + ); export const getLastAddedNotification = createSelector( - getNotifications, - (notifications) => - notifications.reduce((prev, current) => - prev.id > current.id ? prev : current - ) + getNotifications, + (notifications) => + notifications.reduce((prev, current) => + prev.id > current.id ? prev : current + ) ); diff --git a/src/selectors/seriesDetailsSelectors.ts b/src/selectors/seriesDetailsSelectors.ts index f484e37511..f55854c1e7 100644 --- a/src/selectors/seriesDetailsSelectors.ts +++ b/src/selectors/seriesDetailsSelectors.ts @@ -9,22 +9,22 @@ export const getSeriesDetailsAcl = (state: RootState) => state.seriesDetails.acl export const getSeriesDetailsFeeds = (state: RootState) => state.seriesDetails.feeds; export const getSeriesDetailsTheme = (state: RootState) => state.seriesDetails.theme; export const getSeriesDetailsThemeNames = (state: RootState) => - state.seriesDetails.themeNames; + state.seriesDetails.themeNames; export const getSeriesDetailsTobiraData = (state: RootState) => - state.seriesDetails.tobiraData; + state.seriesDetails.tobiraData; export const getSeriesDetailsTobiraStatus = (state: RootState) => - state.seriesDetails.statusTobiraData; + state.seriesDetails.statusTobiraData; export const getSeriesDetailsTobiraDataError = (state: RootState) => - state.seriesDetails.errorTobiraData; + state.seriesDetails.errorTobiraData; export const getTobiraTabHierarchy = (state: RootState) => - state.seriesDetails.tobiraTab; + state.seriesDetails.tobiraTab; /* selectors for statistics */ export const hasStatistics = (state: RootState) => - state.seriesDetails.statistics.length > 0; + state.seriesDetails.statistics.length > 0; export const getStatistics = (state: RootState) => state.seriesDetails.statistics; export const hasStatisticsError = (state: RootState) => - state.seriesDetails.hasStatisticsError; + state.seriesDetails.hasStatisticsError; export const isFetchingStatistics = (state: RootState) => - state.seriesDetails.fetchingStatisticsInProgress; + state.seriesDetails.fetchingStatisticsInProgress; diff --git a/src/selectors/seriesSeletctor.ts b/src/selectors/seriesSeletctor.ts index e0bda76770..fd362bc810 100644 --- a/src/selectors/seriesSeletctor.ts +++ b/src/selectors/seriesSeletctor.ts @@ -14,8 +14,8 @@ export const getSeriesThemes = (state: RootState) => state.series.themes; export const getTotalSeries = (state: RootState) => state.series.total; export const getSeriesTobiraPage = (state: RootState) => - state.series.tobiraPage + state.series.tobiraPage export const getSeriesTobiraPageStatus = (state: RootState) => - state.series.statusTobiraPage + state.series.statusTobiraPage export const getSeriesTobiraPageError = (state: RootState) => - state.series.errorTobiraPage + state.series.errorTobiraPage diff --git a/src/selectors/statisticsSelectors.ts b/src/selectors/statisticsSelectors.ts index 91e45f6635..99d8a58201 100644 --- a/src/selectors/statisticsSelectors.ts +++ b/src/selectors/statisticsSelectors.ts @@ -5,4 +5,4 @@ export const hasStatistics = (state: RootState) => state.statistics.statistics.l export const getStatistics = (state: RootState) => state.statistics.statistics; export const hasStatisticsError = (state: RootState) => state.statistics.hasStatisticsError; export const isFetchingStatistics = (state: RootState) => - state.statistics.status === 'loading'; + state.statistics.status === 'loading'; diff --git a/src/selectors/tableFilterSelectors.ts b/src/selectors/tableFilterSelectors.ts index 667ad87d5e..9fb5d1bfca 100644 --- a/src/selectors/tableFilterSelectors.ts +++ b/src/selectors/tableFilterSelectors.ts @@ -11,4 +11,4 @@ export const getSelectedFilter = (state: RootState) => state.tableFilters.select export const getSecondFilter = (state: RootState) => state.tableFilters.secondFilter; export const getCurrentFilterResource = (state: RootState) => state.tableFilters.currentResource; export const getFilters = (state: RootState, resource: string) => - state.tableFilters.data.filter(obj => obj.resource === resource); + state.tableFilters.data.filter(obj => obj.resource === resource); diff --git a/src/selectors/tableSelectors.ts b/src/selectors/tableSelectors.ts index 65a3f38d41..c51681f203 100644 --- a/src/selectors/tableSelectors.ts +++ b/src/selectors/tableSelectors.ts @@ -18,10 +18,10 @@ export const getTableSorting = (state: RootState) => state.table.sortBy[state.ta export const getTableDirection = (state: RootState) => state.table.reverse[state.table.resource]; export const getTable = (state: RootState) => state.table; export const getDeactivatedColumns = (state: RootState) => - state.table.columns.filter((column) => column.deactivated); + state.table.columns.filter((column) => column.deactivated); export const getActivatedColumns = (state: RootState) => - state.table.columns.filter((column) => !column.deactivated); + state.table.columns.filter((column) => !column.deactivated); export const getSelectedRows = createSelector(getTableRows, (rows) => - rows.filter((row) => row.selected) + rows.filter((row) => row.selected) ); diff --git a/src/selectors/userSelectors.ts b/src/selectors/userSelectors.ts index a9624c054e..d7e2fe2e3b 100644 --- a/src/selectors/userSelectors.ts +++ b/src/selectors/userSelectors.ts @@ -8,5 +8,5 @@ export const getUsers = (state: RootState) => state.users.results; export const getTotalUsers = (state: RootState) => state.users.total; export const getUsernames = createSelector(getUsers, (users) => { - return users.map((user) => user.username); + return users.map((user) => user.username); }); diff --git a/src/selectors/workflowSelectors.ts b/src/selectors/workflowSelectors.ts index 81158cf5d0..392f390275 100644 --- a/src/selectors/workflowSelectors.ts +++ b/src/selectors/workflowSelectors.ts @@ -9,10 +9,10 @@ export const getWorkflowDef = (state: RootState) => state.workflows.workflows; const workflows = (state: RootState) => state.workflows export const getWorkflowDefById = createSelector( - [workflows, (workflows, workflowId: string) => workflowId], - (workflows, workflowId) => { - return workflows.workflows.find( - (workflow) => workflow.id === workflowId - ); - } + [workflows, (workflows, workflowId: string) => workflowId], + (workflows, workflowId) => { + return workflows.workflows.find( + (workflow) => workflow.id === workflowId + ); + } ); diff --git a/src/slices/aclDetailsSlice.ts b/src/slices/aclDetailsSlice.ts index 0d99e13b30..ca0e6bd067 100644 --- a/src/slices/aclDetailsSlice.ts +++ b/src/slices/aclDetailsSlice.ts @@ -9,178 +9,178 @@ import { Acl } from './aclSlice'; * This file contains redux reducer for actions affecting the state of details of an ACL */ export type TransformedAcl = { - actions: string[], - role: string, - read: boolean, - write: boolean + actions: string[], + role: string, + read: boolean, + write: boolean } type AclDetailsState = { - status: 'uninitialized' | 'loading' | 'succeeded' | 'failed', - error: SerializedError | null, + status: 'uninitialized' | 'loading' | 'succeeded' | 'failed', + error: SerializedError | null, organizationId: string, - id: number, - name: string, - acl: TransformedAcl[], + id: number, + name: string, + acl: TransformedAcl[], } // initial redux state const initialState: AclDetailsState = { - status: 'uninitialized', - error: null, - organizationId: "", - id: 0, - name: "", - acl: [], + status: 'uninitialized', + error: null, + organizationId: "", + id: 0, + name: "", + acl: [], }; // fetch details about a certain acl from server export const fetchAclDetails = createAppAsyncThunk('aclDetails/fetchAclDetails', async (aclId: AclDetailsState["id"]) => { - const res = await axios.get(`/admin-ng/acl/${aclId}`); + const res = await axios.get(`/admin-ng/acl/${aclId}`); - let aclDetails = res.data; + let aclDetails = res.data; - let acl: Acl = aclDetails.acl; - let transformedAcls: TransformedAcl[] = []; + let acl: Acl = aclDetails.acl; + let transformedAcls: TransformedAcl[] = []; - // transform policies for further use + // transform policies for further use // We do this in order to prepare the information for the acl tab in the details modals, - // because we render the information differently from how it is usually structured in an ACL - for (let i = 0; acl.ace.length > i; i++) { - if (transformedAcls.find((rule) => rule.role === acl.ace[i].role)) { - for (let j = 0; transformedAcls.length > j; j++) { - // only update entry for policy if already added with other action - if (transformedAcls[j].role === acl.ace[i].role) { - if (acl.ace[i].action === "read") { - transformedAcls[j] = { - ...transformedAcls[j], - read: acl.ace[i].allow, - }; - break; - } - if (acl.ace[i].action === "write") { - transformedAcls[j] = { - ...transformedAcls[j], - write: acl.ace[i].allow, - }; - break; - } - if ( - acl.ace[i].action !== "read" && - acl.ace[i].action !== "write" && - acl.ace[i].allow === true - ) { - transformedAcls[j] = { - ...transformedAcls[j], - actions: transformedAcls[j].actions.concat(acl.ace[i].action), - }; - break; - } - } - } - } else { - // add policy if role not seen before - if (acl.ace[i].action === "read") { - transformedAcls = transformedAcls.concat({ - role: acl.ace[i].role, - read: acl.ace[i].allow, - write: false, - actions: [], - }); - } - if (acl.ace[i].action === "write") { - transformedAcls = transformedAcls.concat({ - role: acl.ace[i].role, - read: false, - write: acl.ace[i].allow, - actions: [], - }); - } - if ( - acl.ace[i].action !== "read" && - acl.ace[i].action !== "write" && - acl.ace[i].allow === true - ) { - transformedAcls = transformedAcls.concat({ - role: acl.ace[i].role, - read: false, - write: false, - actions: [acl.ace[i].action], - }); - } - } - } + // because we render the information differently from how it is usually structured in an ACL + for (let i = 0; acl.ace.length > i; i++) { + if (transformedAcls.find((rule) => rule.role === acl.ace[i].role)) { + for (let j = 0; transformedAcls.length > j; j++) { + // only update entry for policy if already added with other action + if (transformedAcls[j].role === acl.ace[i].role) { + if (acl.ace[i].action === "read") { + transformedAcls[j] = { + ...transformedAcls[j], + read: acl.ace[i].allow, + }; + break; + } + if (acl.ace[i].action === "write") { + transformedAcls[j] = { + ...transformedAcls[j], + write: acl.ace[i].allow, + }; + break; + } + if ( + acl.ace[i].action !== "read" && + acl.ace[i].action !== "write" && + acl.ace[i].allow === true + ) { + transformedAcls[j] = { + ...transformedAcls[j], + actions: transformedAcls[j].actions.concat(acl.ace[i].action), + }; + break; + } + } + } + } else { + // add policy if role not seen before + if (acl.ace[i].action === "read") { + transformedAcls = transformedAcls.concat({ + role: acl.ace[i].role, + read: acl.ace[i].allow, + write: false, + actions: [], + }); + } + if (acl.ace[i].action === "write") { + transformedAcls = transformedAcls.concat({ + role: acl.ace[i].role, + read: false, + write: acl.ace[i].allow, + actions: [], + }); + } + if ( + acl.ace[i].action !== "read" && + acl.ace[i].action !== "write" && + acl.ace[i].allow === true + ) { + transformedAcls = transformedAcls.concat({ + role: acl.ace[i].role, + read: false, + write: false, + actions: [acl.ace[i].action], + }); + } + } + } - aclDetails = { - ...aclDetails, - acl: transformedAcls, - }; + aclDetails = { + ...aclDetails, + acl: transformedAcls, + }; - return aclDetails; + return aclDetails; }); // update details of a certain acl export const updateAclDetails = createAppAsyncThunk('aclDetails/updateAclDetails', async (params: { - values: { - name: string, - acls: TransformedAcl[], - }, - aclId: number, + values: { + name: string, + acls: TransformedAcl[], + }, + aclId: number, }, {dispatch}) => { - const { values, aclId } = params - // transform ACLs back to structure used by backend - let acls = prepareAccessPolicyRulesForPost(values.acls); + const { values, aclId } = params + // transform ACLs back to structure used by backend + let acls = prepareAccessPolicyRulesForPost(values.acls); - // set params for request body - let data = new URLSearchParams(); - data.append("name", values.name); - data.append("acl", JSON.stringify(acls)); + // set params for request body + let data = new URLSearchParams(); + data.append("name", values.name); + data.append("acl", JSON.stringify(acls)); - // PUT request - axios - .put(`/admin-ng/acl/${aclId}`, data) - .then((response) => { - console.info(response); - dispatch(addNotification({type: "success", key: "ACL_UPDATED"})); - }) - .catch((response) => { - console.error(response); - dispatch(addNotification({type: "error", key: "ACL_NOT_SAVED"})); - }); + // PUT request + axios + .put(`/admin-ng/acl/${aclId}`, data) + .then((response) => { + console.info(response); + dispatch(addNotification({type: "success", key: "ACL_UPDATED"})); + }) + .catch((response) => { + console.error(response); + dispatch(addNotification({type: "error", key: "ACL_NOT_SAVED"})); + }); }); const aclDetailsSlice = createSlice({ - name: 'aclDetails', - initialState, - reducers: {}, - // These are used for thunks - extraReducers: builder => { - builder - .addCase(fetchAclDetails.pending, (state) => { - state.status = 'loading'; - }) - .addCase(fetchAclDetails.fulfilled, (state, action: PayloadAction<{ - organizationId: AclDetailsState["organizationId"], - id: AclDetailsState["id"], - name: AclDetailsState["name"], - acl: AclDetailsState["acl"], - }>) => { - state.status = 'succeeded'; - const acls = action.payload; - state.organizationId = acls.organizationId; - state.id = acls.id; - state.name = acls.name; - state.acl = acls.acl; - }) - .addCase(fetchAclDetails.rejected, (state, action) => { - state.status = 'failed'; + name: 'aclDetails', + initialState, + reducers: {}, + // These are used for thunks + extraReducers: builder => { + builder + .addCase(fetchAclDetails.pending, (state) => { + state.status = 'loading'; + }) + .addCase(fetchAclDetails.fulfilled, (state, action: PayloadAction<{ + organizationId: AclDetailsState["organizationId"], + id: AclDetailsState["id"], + name: AclDetailsState["name"], + acl: AclDetailsState["acl"], + }>) => { + state.status = 'succeeded'; + const acls = action.payload; + state.organizationId = acls.organizationId; + state.id = acls.id; + state.name = acls.name; + state.acl = acls.acl; + }) + .addCase(fetchAclDetails.rejected, (state, action) => { + state.status = 'failed'; state.organizationId = ""; state.id = 0; state.name = ""; state.acl = []; - state.error = action.error; - }); - } + state.error = action.error; + }); + } }); // export const {} = aclDetailsSlice.actions; diff --git a/src/slices/aclSlice.ts b/src/slices/aclSlice.ts index a19ff79fb6..441863b442 100644 --- a/src/slices/aclSlice.ts +++ b/src/slices/aclSlice.ts @@ -15,254 +15,254 @@ import { TransformedAcl } from './aclDetailsSlice'; * This file contains redux reducer for actions affecting the state of acls */ export type Ace = { - action: string, - allow: boolean, - role: string, + action: string, + allow: boolean, + role: string, } export type Acl = { - ace: Ace[] + ace: Ace[] } export type Role = { - description: string | undefined, - name: string, - organization: string, - type: string, + description: string | undefined, + name: string, + organization: string, + type: string, } export type AclResult = { - acl: Acl, - id: number, - name: string, - organizationId: string, + acl: Acl, + id: number, + name: string, + organizationId: string, } type AclsState = { - status: 'uninitialized' | 'loading' | 'succeeded' | 'failed', - error: SerializedError | null, - results: AclResult[], - columns: TableConfig["columns"], - total: number, - count: number, - offset: number, - limit: number, + status: 'uninitialized' | 'loading' | 'succeeded' | 'failed', + error: SerializedError | null, + results: AclResult[], + columns: TableConfig["columns"], + total: number, + count: number, + offset: number, + limit: number, }; // Fill columns initially with columns defined in aclsTableConfig const initialColumns = aclsTableConfig.columns.map((column) => ({ - ...column, - deactivated: false, + ...column, + deactivated: false, })); // Initial state of acls in redux store const initialState: AclsState = { - status: 'uninitialized', - error: null, - results: [], - columns: initialColumns, - total: 0, - count: 0, - offset: 0, - limit: 0, + status: 'uninitialized', + error: null, + results: [], + columns: initialColumns, + total: 0, + count: 0, + offset: 0, + limit: 0, }; export const fetchAcls = createAppAsyncThunk('acls/fetchAcls', async (_, { getState }) => { - const state = getState(); - let params = getURLParams(state, "acls"); - // Just make the async request here, and return the response. - // This will automatically dispatch a `pending` action first, - // and then `fulfilled` or `rejected` actions based on the promise. - const res = await axios.get("/admin-ng/acl/acls.json", { params: params }); - return res.data; + const state = getState(); + let params = getURLParams(state, "acls"); + // Just make the async request here, and return the response. + // This will automatically dispatch a `pending` action first, + // and then `fulfilled` or `rejected` actions based on the promise. + const res = await axios.get("/admin-ng/acl/acls.json", { params: params }); + return res.data; }); // todo: unite following in one fetch method (maybe also move to own file containing all fetches regarding resources endpoint) // get acl templates export const fetchAclTemplates = async () => { - let data = await axios.get("/admin-ng/resources/ACL.json"); + let data = await axios.get("/admin-ng/resources/ACL.json"); - const response = await data.data; + const response = await data.data; - return transformToIdValueArray(response); + return transformToIdValueArray(response); }; // fetch additional actions that a policy allows user to perform on an event export const fetchAclActions = async () => { - let data = await axios.get("/admin-ng/resources/ACL.ACTIONS.json"); + let data = await axios.get("/admin-ng/resources/ACL.ACTIONS.json"); - const response = await data.data; + const response = await data.data; - const actions = transformToIdValueArray(response); + const actions = transformToIdValueArray(response); - return actions; + return actions; }; // fetch defaults for the access policy tab in the details views export const fetchAclDefaults = async () => { - let data = await axios.get("/admin-ng/resources/ACL.DEFAULTS.json"); + let data = await axios.get("/admin-ng/resources/ACL.DEFAULTS.json"); - const response = await data.data; + const response = await data.data; - return response; + return response; }; // fetch all policies of an certain acl template export const fetchAclTemplateById = async (id: string) => { - let response = await axios.get(`/acl-manager/acl/${id}`); + let response = await axios.get(`/acl-manager/acl/${id}`); - let acl = response.data.acl; + let acl = response.data.acl; - return transformAclTemplatesResponse(acl); + return transformAclTemplatesResponse(acl); }; // fetch roles for select dialogs and access policy pages export const fetchRolesWithTarget = async (target: string) => { - let params = { - limit: -1, - target: target, - }; + let params = { + limit: -1, + target: target, + }; - let response = await axios.get("/admin-ng/acl/roles.json", { params: params }); - let data : Role[] = response.data + let response = await axios.get("/admin-ng/acl/roles.json", { params: params }); + let data : Role[] = response.data - return await data; + return await data; }; // post new acl to backend export const postNewAcl = (values: typeof initialFormValuesNewAcl) => async (dispatch: AppDispatch) => { - let acls = prepareAccessPolicyRulesForPost(values.acls); - - let data = new URLSearchParams(); - data.append("name", values.name); - data.append("acl", JSON.stringify(acls)); - - axios - .post("/admin-ng/acl", data, { - headers: { - "Content-Type": "application/x-www-form-urlencoded", - }, - }) - .then((response) => { - console.info(response); - dispatch(addNotification({type: "success", key: "ACL_ADDED"})); - }) - .catch((response) => { - console.error(response); - dispatch(addNotification({type: "error", key: "ACL_NOT_SAVED"})); - }); + let acls = prepareAccessPolicyRulesForPost(values.acls); + + let data = new URLSearchParams(); + data.append("name", values.name); + data.append("acl", JSON.stringify(acls)); + + axios + .post("/admin-ng/acl", data, { + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + }) + .then((response) => { + console.info(response); + dispatch(addNotification({type: "success", key: "ACL_ADDED"})); + }) + .catch((response) => { + console.error(response); + dispatch(addNotification({type: "error", key: "ACL_NOT_SAVED"})); + }); }; // delete acl with provided id export const deleteAcl = (id: number) => async (dispatch: AppDispatch) => { - axios - .delete(`/admin-ng/acl/${id}`) - .then((res) => { - console.info(res); - //add success notification - dispatch(addNotification({type: "success", key: "ACL_DELETED"})); - }) - .catch((res) => { - console.error(res); - // add error notification - dispatch(addNotification({type: "error", key: "ACL_NOT_DELETED"})); - }); + axios + .delete(`/admin-ng/acl/${id}`) + .then((res) => { + console.info(res); + //add success notification + dispatch(addNotification({type: "success", key: "ACL_DELETED"})); + }) + .catch((res) => { + console.error(res); + // add error notification + dispatch(addNotification({type: "error", key: "ACL_NOT_DELETED"})); + }); }; // @ts-expect-error TS(7006): export const checkAcls = (acls: TransformedAcl[]) => async (dispatch: AppDispatch, getState) => { - // Remove old notifications of context event-access - // Helps to prevent multiple notifications for same problem - dispatch(removeNotificationWizardAccess()); - - let user = getUserInformation(getState()); - - let check = true; - let bothRights = false; - - for (let i = 0; acls.length > i; i++) { - // check if a role is chosen - if (acls[i].role === "") { - check = false; - } - - // if not admin, check if there is at least one policy with read and write rights - if ((acls[i].read && acls[i].write) || user.isAdmin) { - bothRights = true; - } - - // check if each policy has read or write right (at least one checkbox should be checked) - if (!acls[i].read && !acls[i].write) { - check = false; - } - } - - if (!check) { - dispatch( - addNotification({ - type: "warning", - key: "INVALID_ACL_RULES", - duration: -1, - parameter: undefined, - context: NOTIFICATION_CONTEXT_ACCESS - }) - ); - } - - if (!bothRights) { - dispatch( - addNotification({ - type: "warning", - key: "MISSING_ACL_RULES", - duration: -1, - parameter: undefined, - context: NOTIFICATION_CONTEXT_ACCESS - }) - ); - check = false; - } - - return check; + // Remove old notifications of context event-access + // Helps to prevent multiple notifications for same problem + dispatch(removeNotificationWizardAccess()); + + let user = getUserInformation(getState()); + + let check = true; + let bothRights = false; + + for (let i = 0; acls.length > i; i++) { + // check if a role is chosen + if (acls[i].role === "") { + check = false; + } + + // if not admin, check if there is at least one policy with read and write rights + if ((acls[i].read && acls[i].write) || user.isAdmin) { + bothRights = true; + } + + // check if each policy has read or write right (at least one checkbox should be checked) + if (!acls[i].read && !acls[i].write) { + check = false; + } + } + + if (!check) { + dispatch( + addNotification({ + type: "warning", + key: "INVALID_ACL_RULES", + duration: -1, + parameter: undefined, + context: NOTIFICATION_CONTEXT_ACCESS + }) + ); + } + + if (!bothRights) { + dispatch( + addNotification({ + type: "warning", + key: "MISSING_ACL_RULES", + duration: -1, + parameter: undefined, + context: NOTIFICATION_CONTEXT_ACCESS + }) + ); + check = false; + } + + return check; }; const aclsSlice = createSlice({ - name: 'acls', - initialState, - reducers: { - setAclColumns(state, action: PayloadAction< - AclsState["columns"] - >) { - state.columns = action.payload; - }, - }, - // These are used for thunks - extraReducers: builder => { - builder - .addCase(fetchAcls.pending, (state) => { - state.status = 'loading'; - }) - // Pass the generated action creators to `.addCase()` - .addCase(fetchAcls.fulfilled, (state, action: PayloadAction<{ - total: AclsState["total"], - count: AclsState["count"], - limit: AclsState["limit"], - offset: AclsState["offset"], - results: AclsState["results"], - }>) => { - // Same "mutating" update syntax thanks to Immer - state.status = 'succeeded'; - const acls = action.payload; - state.total = acls.total; - state.count = acls.count; - state.limit = acls.limit; - state.offset = acls.offset; - state.results = acls.results; - }) - .addCase(fetchAcls.rejected, (state, action) => { - state.status = 'failed'; - state.results = []; - state.error = action.error; - }); - } + name: 'acls', + initialState, + reducers: { + setAclColumns(state, action: PayloadAction< + AclsState["columns"] + >) { + state.columns = action.payload; + }, + }, + // These are used for thunks + extraReducers: builder => { + builder + .addCase(fetchAcls.pending, (state) => { + state.status = 'loading'; + }) + // Pass the generated action creators to `.addCase()` + .addCase(fetchAcls.fulfilled, (state, action: PayloadAction<{ + total: AclsState["total"], + count: AclsState["count"], + limit: AclsState["limit"], + offset: AclsState["offset"], + results: AclsState["results"], + }>) => { + // Same "mutating" update syntax thanks to Immer + state.status = 'succeeded'; + const acls = action.payload; + state.total = acls.total; + state.count = acls.count; + state.limit = acls.limit; + state.offset = acls.offset; + state.results = acls.results; + }) + .addCase(fetchAcls.rejected, (state, action) => { + state.status = 'failed'; + state.results = []; + state.error = action.error; + }); + } }); export const { setAclColumns } = aclsSlice.actions; diff --git a/src/slices/eventDetailsSlice.ts b/src/slices/eventDetailsSlice.ts index 3f1ebca98f..f72f712da0 100644 --- a/src/slices/eventDetailsSlice.ts +++ b/src/slices/eventDetailsSlice.ts @@ -2,23 +2,23 @@ import { PayloadAction, SerializedError, createSlice, unwrapResult } from '@redu import axios from 'axios'; import { addNotification, removeNotificationWizardForm } from "./notificationSlice"; import { - createPolicy, - getHttpHeaders, - transformMetadataCollection, - transformMetadataForUpdate, + createPolicy, + getHttpHeaders, + transformMetadataCollection, + transformMetadataForUpdate, } from "../utils/resourceUtils"; import { NOTIFICATION_CONTEXT } from "../configs/modalConfig"; import { fetchWorkflowDef, Workflow as WorkflowDefinitions } from "./workflowSlice"; import { - getExtendedMetadata, - getSchedulingSource, - getWorkflows, - getStatistics, + getExtendedMetadata, + getSchedulingSource, + getWorkflows, + getStatistics, } from "../selectors/eventDetailsSelectors"; import { getWorkflowDef } from "../selectors/workflowSelectors"; import { - getAssetUploadOptions, - getAssetUploadWorkflow, + getAssetUploadOptions, + getAssetUploadWorkflow, } from "../selectors/eventSelectors"; import { calculateDuration } from "../utils/dateUtils"; import { fetchRecordings } from "./recordingSlice"; @@ -30,9 +30,9 @@ import { TransformedAcl } from './aclDetailsSlice'; import { MetadataCatalog } from './eventSlice'; import { Event } from "./eventSlice"; import { - AssetTabHierarchy, - EventDetailsPage, - WorkflowTabHierarchy + AssetTabHierarchy, + EventDetailsPage, + WorkflowTabHierarchy } from "../components/events/partials/modals/EventDetails"; import { AppDispatch } from "../store"; import { Ace } from './aclSlice'; @@ -41,1135 +41,1135 @@ import { handleTobiraError } from './shared/tobiraErrors'; // Contains the navigation logic for the modal type EventDetailsModal = { - show: boolean, - page: EventDetailsPage, - event: Event | null, - workflowTabHierarchy: WorkflowTabHierarchy, - assetsTabHierarchy: AssetTabHierarchy, - workflowId: string, + show: boolean, + page: EventDetailsPage, + event: Event | null, + workflowTabHierarchy: WorkflowTabHierarchy, + assetsTabHierarchy: AssetTabHierarchy, + workflowId: string, } interface Assets { - id: string, - mimetype: string, - tags: string[], - url: string, + id: string, + mimetype: string, + tags: string[], + url: string, } type AssetDetails = { - id: string, - type: string, - mimetype: string, - size: number, - tags: string[], - url: string, - checksum: string | undefined, - reference: string, + id: string, + type: string, + mimetype: string, + size: number, + tags: string[], + url: string, + checksum: string | undefined, + reference: string, } type CommentAuthor = { - email: string | undefined, - name: string, - username: string, + email: string | undefined, + name: string, + username: string, } type Workflow = { - scheduling: boolean, - entries: { - id: string, - status: string, //translation key - submitted: string, //date - submitter: string, - submitterEmail: string, - submitterName: string, - title: string - }[], - // TODO: This looks like really bad practice. Rewrite. - workflow: { // The type when looking at the list of workflows - workflowId: string, - description?: string, - configuration?: { [key: string]: unknown } - } | { // The type when looking at the details of a particular workflow - configuration: { [key: string]: unknown } - creator: string - description: string - executionTime: number - status: string // translation string - submittedAt: string // date string - title: string - wdid: string - wiid: number - } + scheduling: boolean, + entries: { + id: string, + status: string, //translation key + submitted: string, //date + submitter: string, + submitterEmail: string, + submitterName: string, + title: string + }[], + // TODO: This looks like really bad practice. Rewrite. + workflow: { // The type when looking at the list of workflows + workflowId: string, + description?: string, + configuration?: { [key: string]: unknown } + } | { // The type when looking at the details of a particular workflow + configuration: { [key: string]: unknown } + creator: string + description: string + executionTime: number + status: string // translation string + submittedAt: string // date string + title: string + wdid: string + wiid: number + } } type Device = { - id: string, - inputs: { id: string, value: string }[], - inputMethods: string[], - name: string, - // Fields we add to "device" from recordings but don't actually care about? - // removable: boolean, - // roomId: string, - // status: string, - // type: string, - // updated: string, - // url: string, + id: string, + inputs: { id: string, value: string }[], + inputMethods: string[], + name: string, + // Fields we add to "device" from recordings but don't actually care about? + // removable: boolean, + // roomId: string, + // status: string, + // type: string, + // updated: string, + // url: string, } export type UploadAssetOption = { - id: string, - title: string, // translation key - type: string, // "track", "attachment" etc. - flavorType: string, - flavorSubType: string, - accept: string, - displayOrder: number, + id: string, + title: string, // translation key + type: string, // "track", "attachment" etc. + flavorType: string, + flavorSubType: string, + accept: string, + displayOrder: number, } export type Publication = { - enabled: boolean, - icon?: string, - id: string, - label?: string, - hide?: boolean, - name: string, // translation key - order: number, - url: string, - description?: string, + enabled: boolean, + icon?: string, + id: string, + label?: string, + hide?: boolean, + name: string, // translation key + order: number, + url: string, + description?: string, } export type Comment = { - author: CommentAuthor, - creationDate: string, - id: number, - modificationDate: string, - reason: string, // translation key - replies: CommentReply[], - resolvedStatus: boolean, - text: string, + author: CommentAuthor, + creationDate: string, + id: number, + modificationDate: string, + reason: string, // translation key + replies: CommentReply[], + resolvedStatus: boolean, + text: string, } export type CommentReply = { - author: CommentAuthor, - creationDate: string, - id: number, - modificationDate: string, - text: string, + author: CommentAuthor, + creationDate: string, + id: number, + modificationDate: string, + text: string, } type EventDetailsState = { - statusMetadata: 'uninitialized' | 'loading' | 'succeeded' | 'failed', - errorMetadata: SerializedError | null, - statusAssets: 'uninitialized' | 'loading' | 'succeeded' | 'failed', - errorAssets: SerializedError | null, - statusAssetAttachments: 'uninitialized' | 'loading' | 'succeeded' | 'failed', // These were previously all just statusAssets - errorAssetAttachments: SerializedError | null, - statusAssetAttachmentDetails: 'uninitialized' | 'loading' | 'succeeded' | 'failed', - errorAssetAttachmentDetails: SerializedError | null, - statusAssetCatalogs: 'uninitialized' | 'loading' | 'succeeded' | 'failed', - errorAssetCatalogs: SerializedError | null, - statusAssetCatalogDetails: 'uninitialized' | 'loading' | 'succeeded' | 'failed', - errorAssetCatalogDetails: SerializedError | null, - statusAssetMedia: 'uninitialized' | 'loading' | 'succeeded' | 'failed', - errorAssetMedia: SerializedError | null, - statusAssetMediaDetails: 'uninitialized' | 'loading' | 'succeeded' | 'failed', - errorAssetMediaDetails: SerializedError | null, - statusAssetPublications: 'uninitialized' | 'loading' | 'succeeded' | 'failed', - errorAssetPublications: SerializedError | null, - statusAssetPublicationDetails: 'uninitialized' | 'loading' | 'succeeded' | 'failed', - errorAssetPublicationDetails: SerializedError | null, - statusPolicies: 'uninitialized' | 'loading' | 'succeeded' | 'failed', - errorPolicies: SerializedError | null, - statusComments: 'uninitialized' | 'loading' | 'succeeded' | 'failed', - errorComments: SerializedError | null, - statusPublications: 'uninitialized' | 'loading' | 'succeeded' | 'failed', - errorPublications: SerializedError | null, - statusSaveComment: 'uninitialized' | 'loading' | 'succeeded' | 'failed', - errorSaveComment: SerializedError | null, - statusSaveCommentReply: 'uninitialized' | 'loading' | 'succeeded' | 'failed', - errorSaveCommentReply: SerializedError | null, - statusUpdateComment: 'uninitialized' | 'loading' | 'succeeded' | 'failed', - errorUpdateComment: SerializedError | null, - statusScheduling: 'uninitialized' | 'loading' | 'succeeded' | 'failed', - errorScheduling: SerializedError | null, - statusSaveScheduling: 'uninitialized' | 'loading' | 'succeeded' | 'failed', - errorSaveScheduling: SerializedError | null, - statusCheckConflicts: 'uninitialized' | 'loading' | 'succeeded' | 'failed', - errorCheckConflicts: SerializedError | null, - statusWorkflows: 'uninitialized' | 'loading' | 'succeeded' | 'failed', - errorWorkflows: SerializedError | null, - statusWorkflowDetails: 'uninitialized' | 'loading' | 'succeeded' | 'failed', - errorWorkflowDetails: SerializedError | null, - statusDoWorkflowAction: 'uninitialized' | 'loading' | 'succeeded' | 'failed', - errorDoWorkflowAction: SerializedError | null, - statusDeleteWorkflow: 'uninitialized' | 'loading' | 'succeeded' | 'failed', - errorDeleteWorkflow: SerializedError | null, - statusWorkflowOperations: 'uninitialized' | 'loading' | 'succeeded' | 'failed', - errorWorkflowOperations: SerializedError | null, - statusWorkflowOperationDetails: 'uninitialized' | 'loading' | 'succeeded' | 'failed', - errorWorkflowOperationDetails: SerializedError | null, - statusWorkflowErrors: 'uninitialized' | 'loading' | 'succeeded' | 'failed', - errorWorkflowErrors: SerializedError | null, - statusWorkflowErrorDetails: 'uninitialized' | 'loading' | 'succeeded' | 'failed', - errorWorkflowErrorDetails: SerializedError | null, - statusStatistics: 'uninitialized' | 'loading' | 'succeeded' | 'failed', - errorStatistics: SerializedError | null, - statusStatisticsValue: 'uninitialized' | 'loading' | 'succeeded' | 'failed', - errorStatisticsValue: SerializedError | null, - statusTobiraData: 'uninitialized' | 'loading' | 'succeeded' | 'failed', - errorTobiraData: SerializedError | null, - eventId: string, - modal: EventDetailsModal, - metadata: MetadataCatalog, - extendedMetadata: MetadataCatalog[], - assets: { - attachments: number, - catalogs: number, - media: number, - publications: number, - }, - transactionsReadOnly: boolean, - uploadAssetOptions: UploadAssetOption[] | undefined, - assetAttachments: Array< Assets & { - type: string, - }>, - assetAttachmentDetails: AssetDetails, - assetCatalogs: Array< Assets & { - type: string, - }>, - assetCatalogDetails: AssetDetails, - assetMedia: Array< Assets & { - type: string, - mediaFileName: string, - }>, - assetMediaDetails: AssetDetails & { - duration: number, - has_audio: boolean, - has_subtitle: boolean, - has_video: boolean, - streams: { - audio: { - bitdepth: string, - bitrate: number, - channels: number, - framecount: number, - id: string, - peakleveldb: string, - rmsleveldb: string, - rmspeakdb: "", - samplingrate: number, - type: string, - }[], - video: { - bitrate: number, - framecount: number, - framerate: number, - id: string, - resolution: string, - scanorder: string, - scantype: string, - type: string, - }[], - }, - video: { - previews: { - uri: string, - }[] - url: string, - } | undefined, - }, - assetPublications: Array< Assets & { - channel: string, - }>, - assetPublicationDetails: AssetDetails & { - channel: string, - }, - policies: TransformedAcl[], - comments: Comment[], - commentReasons: { [key: string]: string }, - scheduling: { - hasProperties: boolean, - }, - schedulingSource: { - start: { - date: string, - hour: number | undefined, - minute: number | undefined, - }, - duration: { - hour: number | undefined, - minute: number | undefined, - }, - end: { - date: string, - hour: number | undefined, - minute: number | undefined, - }, - device: Device, - agentId: string | undefined, - agentConfiguration: { [key: string]: string }, - }, - hasSchedulingConflicts: boolean, - schedulingConflicts: { - title: string, - start: string, - end: string, - }[], - workflows: Workflow, - workflowConfiguration: { - workflowId: string, - description?: string, - }, - workflowDefinitions: WorkflowDefinitions[], - baseWorkflow: { - workflowId: string, - description?: string, - configuration?: {[key: string]: unknown} - }, - workflowOperations: { - entries: { - configuration: { [key: string]: string }, - description: string, - id: number, - status: string, // translation key - title: string, - }[] - }, - workflowOperationDetails: { - completed: string, // date - description: string, - exception_handler_workflow: string, - execution_host: string, - fail_on_error: boolean, - failed_attempts: number, - job: number, - max_attempts: number, - name: string, - retry_strategy: string, - started: string, // date - state: string, // translation key - time_in_queue: number, - }, - workflowErrors: { - entries: { - description: string, - id: number, - severity: string, - timestamp: string, // date - title: string, - }[] - }, - workflowErrorDetails: { - description: string, - details: { - name: string, - value: string, - }[], - id: number, - job_id: number, - processing_host: string, - service_type: string, - severity: string, - technical_details: string, - timestamp: string, // date - title: string, - }, - publications: Publication[], - statistics: Statistics[], - hasStatisticsError: boolean, - tobiraData: TobiraData, + statusMetadata: 'uninitialized' | 'loading' | 'succeeded' | 'failed', + errorMetadata: SerializedError | null, + statusAssets: 'uninitialized' | 'loading' | 'succeeded' | 'failed', + errorAssets: SerializedError | null, + statusAssetAttachments: 'uninitialized' | 'loading' | 'succeeded' | 'failed', // These were previously all just statusAssets + errorAssetAttachments: SerializedError | null, + statusAssetAttachmentDetails: 'uninitialized' | 'loading' | 'succeeded' | 'failed', + errorAssetAttachmentDetails: SerializedError | null, + statusAssetCatalogs: 'uninitialized' | 'loading' | 'succeeded' | 'failed', + errorAssetCatalogs: SerializedError | null, + statusAssetCatalogDetails: 'uninitialized' | 'loading' | 'succeeded' | 'failed', + errorAssetCatalogDetails: SerializedError | null, + statusAssetMedia: 'uninitialized' | 'loading' | 'succeeded' | 'failed', + errorAssetMedia: SerializedError | null, + statusAssetMediaDetails: 'uninitialized' | 'loading' | 'succeeded' | 'failed', + errorAssetMediaDetails: SerializedError | null, + statusAssetPublications: 'uninitialized' | 'loading' | 'succeeded' | 'failed', + errorAssetPublications: SerializedError | null, + statusAssetPublicationDetails: 'uninitialized' | 'loading' | 'succeeded' | 'failed', + errorAssetPublicationDetails: SerializedError | null, + statusPolicies: 'uninitialized' | 'loading' | 'succeeded' | 'failed', + errorPolicies: SerializedError | null, + statusComments: 'uninitialized' | 'loading' | 'succeeded' | 'failed', + errorComments: SerializedError | null, + statusPublications: 'uninitialized' | 'loading' | 'succeeded' | 'failed', + errorPublications: SerializedError | null, + statusSaveComment: 'uninitialized' | 'loading' | 'succeeded' | 'failed', + errorSaveComment: SerializedError | null, + statusSaveCommentReply: 'uninitialized' | 'loading' | 'succeeded' | 'failed', + errorSaveCommentReply: SerializedError | null, + statusUpdateComment: 'uninitialized' | 'loading' | 'succeeded' | 'failed', + errorUpdateComment: SerializedError | null, + statusScheduling: 'uninitialized' | 'loading' | 'succeeded' | 'failed', + errorScheduling: SerializedError | null, + statusSaveScheduling: 'uninitialized' | 'loading' | 'succeeded' | 'failed', + errorSaveScheduling: SerializedError | null, + statusCheckConflicts: 'uninitialized' | 'loading' | 'succeeded' | 'failed', + errorCheckConflicts: SerializedError | null, + statusWorkflows: 'uninitialized' | 'loading' | 'succeeded' | 'failed', + errorWorkflows: SerializedError | null, + statusWorkflowDetails: 'uninitialized' | 'loading' | 'succeeded' | 'failed', + errorWorkflowDetails: SerializedError | null, + statusDoWorkflowAction: 'uninitialized' | 'loading' | 'succeeded' | 'failed', + errorDoWorkflowAction: SerializedError | null, + statusDeleteWorkflow: 'uninitialized' | 'loading' | 'succeeded' | 'failed', + errorDeleteWorkflow: SerializedError | null, + statusWorkflowOperations: 'uninitialized' | 'loading' | 'succeeded' | 'failed', + errorWorkflowOperations: SerializedError | null, + statusWorkflowOperationDetails: 'uninitialized' | 'loading' | 'succeeded' | 'failed', + errorWorkflowOperationDetails: SerializedError | null, + statusWorkflowErrors: 'uninitialized' | 'loading' | 'succeeded' | 'failed', + errorWorkflowErrors: SerializedError | null, + statusWorkflowErrorDetails: 'uninitialized' | 'loading' | 'succeeded' | 'failed', + errorWorkflowErrorDetails: SerializedError | null, + statusStatistics: 'uninitialized' | 'loading' | 'succeeded' | 'failed', + errorStatistics: SerializedError | null, + statusStatisticsValue: 'uninitialized' | 'loading' | 'succeeded' | 'failed', + errorStatisticsValue: SerializedError | null, + statusTobiraData: 'uninitialized' | 'loading' | 'succeeded' | 'failed', + errorTobiraData: SerializedError | null, + eventId: string, + modal: EventDetailsModal, + metadata: MetadataCatalog, + extendedMetadata: MetadataCatalog[], + assets: { + attachments: number, + catalogs: number, + media: number, + publications: number, + }, + transactionsReadOnly: boolean, + uploadAssetOptions: UploadAssetOption[] | undefined, + assetAttachments: Array< Assets & { + type: string, + }>, + assetAttachmentDetails: AssetDetails, + assetCatalogs: Array< Assets & { + type: string, + }>, + assetCatalogDetails: AssetDetails, + assetMedia: Array< Assets & { + type: string, + mediaFileName: string, + }>, + assetMediaDetails: AssetDetails & { + duration: number, + has_audio: boolean, + has_subtitle: boolean, + has_video: boolean, + streams: { + audio: { + bitdepth: string, + bitrate: number, + channels: number, + framecount: number, + id: string, + peakleveldb: string, + rmsleveldb: string, + rmspeakdb: "", + samplingrate: number, + type: string, + }[], + video: { + bitrate: number, + framecount: number, + framerate: number, + id: string, + resolution: string, + scanorder: string, + scantype: string, + type: string, + }[], + }, + video: { + previews: { + uri: string, + }[] + url: string, + } | undefined, + }, + assetPublications: Array< Assets & { + channel: string, + }>, + assetPublicationDetails: AssetDetails & { + channel: string, + }, + policies: TransformedAcl[], + comments: Comment[], + commentReasons: { [key: string]: string }, + scheduling: { + hasProperties: boolean, + }, + schedulingSource: { + start: { + date: string, + hour: number | undefined, + minute: number | undefined, + }, + duration: { + hour: number | undefined, + minute: number | undefined, + }, + end: { + date: string, + hour: number | undefined, + minute: number | undefined, + }, + device: Device, + agentId: string | undefined, + agentConfiguration: { [key: string]: string }, + }, + hasSchedulingConflicts: boolean, + schedulingConflicts: { + title: string, + start: string, + end: string, + }[], + workflows: Workflow, + workflowConfiguration: { + workflowId: string, + description?: string, + }, + workflowDefinitions: WorkflowDefinitions[], + baseWorkflow: { + workflowId: string, + description?: string, + configuration?: {[key: string]: unknown} + }, + workflowOperations: { + entries: { + configuration: { [key: string]: string }, + description: string, + id: number, + status: string, // translation key + title: string, + }[] + }, + workflowOperationDetails: { + completed: string, // date + description: string, + exception_handler_workflow: string, + execution_host: string, + fail_on_error: boolean, + failed_attempts: number, + job: number, + max_attempts: number, + name: string, + retry_strategy: string, + started: string, // date + state: string, // translation key + time_in_queue: number, + }, + workflowErrors: { + entries: { + description: string, + id: number, + severity: string, + timestamp: string, // date + title: string, + }[] + }, + workflowErrorDetails: { + description: string, + details: { + name: string, + value: string, + }[], + id: number, + job_id: number, + processing_host: string, + service_type: string, + severity: string, + technical_details: string, + timestamp: string, // date + title: string, + }, + publications: Publication[], + statistics: Statistics[], + hasStatisticsError: boolean, + tobiraData: TobiraData, } // Initial state of event details in redux store const initialState: EventDetailsState = { - statusMetadata: 'uninitialized', - errorMetadata: null, - statusAssets: 'uninitialized', - errorAssets: null, - statusAssetAttachments: 'uninitialized', - errorAssetAttachments: null, - statusAssetAttachmentDetails: 'uninitialized', - errorAssetAttachmentDetails: null, - statusAssetCatalogs: 'uninitialized', - errorAssetCatalogs: null, - statusAssetCatalogDetails: 'uninitialized', - errorAssetCatalogDetails: null, - statusAssetMedia: 'uninitialized', - errorAssetMedia: null, - statusAssetMediaDetails: 'uninitialized', - errorAssetMediaDetails: null, - statusAssetPublications: 'uninitialized', - errorAssetPublications: null, - statusAssetPublicationDetails: 'uninitialized', - errorAssetPublicationDetails: null, - statusPolicies: 'uninitialized', - errorPolicies: null, - statusComments: 'uninitialized', - errorComments: null, - statusPublications: 'uninitialized', - errorPublications: null, - statusSaveComment: 'uninitialized', - errorSaveComment: null, - statusSaveCommentReply: 'uninitialized', - errorSaveCommentReply: null, - statusUpdateComment: 'uninitialized', - errorUpdateComment: null, - statusScheduling: 'uninitialized', - errorScheduling: null, - statusSaveScheduling: 'uninitialized', - errorSaveScheduling: null, - statusCheckConflicts: 'uninitialized', - errorCheckConflicts: null, - statusWorkflows: 'uninitialized', - errorWorkflows: null, - statusWorkflowDetails: 'uninitialized', - errorWorkflowDetails: null, - statusDoWorkflowAction: 'uninitialized', - errorDoWorkflowAction: null, - statusDeleteWorkflow: 'uninitialized', - errorDeleteWorkflow: null, - statusWorkflowOperations: 'uninitialized', - errorWorkflowOperations: null, - statusWorkflowOperationDetails: 'uninitialized', - errorWorkflowOperationDetails: null, - statusWorkflowErrors: 'uninitialized', - errorWorkflowErrors: null, - statusWorkflowErrorDetails: 'uninitialized', - errorWorkflowErrorDetails: null, - statusStatistics: 'uninitialized', - errorStatistics: null, - statusStatisticsValue: 'uninitialized', - errorStatisticsValue: null, - statusTobiraData: 'uninitialized', - errorTobiraData: null, - eventId: "", - modal: { - show: false, - page: EventDetailsPage.Metadata, - event: null, - workflowTabHierarchy: 'entry', - assetsTabHierarchy: 'entry', - workflowId: "", - }, - metadata: { - title: "", - flavor: "", - fields: [], - }, - extendedMetadata: [], - assets: { - attachments: 0, - catalogs: 0, - media: 0, - publications: 0, - }, - transactionsReadOnly: false, - uploadAssetOptions: [], - assetAttachments: [], - assetAttachmentDetails: { - id: "", - type: "", - mimetype: "", - size: 0, - checksum: undefined, - reference: "", - tags: [], - url: "", - }, - assetCatalogs: [], - assetCatalogDetails: { - id: "", - type: "", - mimetype: "", - size: 0, - checksum: undefined, - reference: "", - tags: [], - url: "", - }, - assetMedia: [], - assetMediaDetails: { - id: "", - type: "", - mimetype: "", - tags: [], - duration: 0, - size: 0, - checksum: undefined, - reference: "", - has_audio: false, - has_subtitle: false, - has_video: false, - url: "", - streams: { - audio: [], - video: [], - }, - video: undefined, - }, - assetPublications: [], - assetPublicationDetails: { - id: "", - type: "", - mimetype: "", - size: 0, - channel: "", - checksum: undefined, - reference: "", - tags: [], - url: "", - }, - policies: [], - comments: [], - commentReasons: {}, - scheduling: { - hasProperties: false, - }, - schedulingSource: { - start: { - date: "", - hour: undefined, - minute: undefined, - }, - duration: { - hour: undefined, - minute: undefined, - }, - end: { - date: "", - hour: undefined, - minute: undefined, - }, - device: { - id: "", - name: "", - inputs: [], - inputMethods: [], - }, - agentId: undefined, - agentConfiguration: {}, - }, - hasSchedulingConflicts: false, - schedulingConflicts: [], - workflows: { - scheduling: false, - entries: [], - workflow: { - workflowId: "", - description: "", - }, - }, - workflowConfiguration: { - workflowId: "", - description: "", - }, - workflowDefinitions: [], - baseWorkflow: { - workflowId: "" - }, - workflowOperations: { - entries: [], - }, - workflowOperationDetails: { - completed: "", - description: "", - exception_handler_workflow: "", - execution_host: "", - fail_on_error: false, - failed_attempts: 0, - job: 0, - max_attempts: 0, - name: "", - retry_strategy: "", - started: "", - state: "", - time_in_queue: 0, - }, - workflowErrors: { - entries: [] - }, - workflowErrorDetails: { - description: "", - details: [], - id: 0, - job_id: 0, - processing_host: "", - service_type: "", - severity: "", - technical_details: "", - timestamp: "", - title: "", - }, - publications: [], - statistics: [], - hasStatisticsError: false, - tobiraData: { - baseURL: "", - hostPages: [], - }, + statusMetadata: 'uninitialized', + errorMetadata: null, + statusAssets: 'uninitialized', + errorAssets: null, + statusAssetAttachments: 'uninitialized', + errorAssetAttachments: null, + statusAssetAttachmentDetails: 'uninitialized', + errorAssetAttachmentDetails: null, + statusAssetCatalogs: 'uninitialized', + errorAssetCatalogs: null, + statusAssetCatalogDetails: 'uninitialized', + errorAssetCatalogDetails: null, + statusAssetMedia: 'uninitialized', + errorAssetMedia: null, + statusAssetMediaDetails: 'uninitialized', + errorAssetMediaDetails: null, + statusAssetPublications: 'uninitialized', + errorAssetPublications: null, + statusAssetPublicationDetails: 'uninitialized', + errorAssetPublicationDetails: null, + statusPolicies: 'uninitialized', + errorPolicies: null, + statusComments: 'uninitialized', + errorComments: null, + statusPublications: 'uninitialized', + errorPublications: null, + statusSaveComment: 'uninitialized', + errorSaveComment: null, + statusSaveCommentReply: 'uninitialized', + errorSaveCommentReply: null, + statusUpdateComment: 'uninitialized', + errorUpdateComment: null, + statusScheduling: 'uninitialized', + errorScheduling: null, + statusSaveScheduling: 'uninitialized', + errorSaveScheduling: null, + statusCheckConflicts: 'uninitialized', + errorCheckConflicts: null, + statusWorkflows: 'uninitialized', + errorWorkflows: null, + statusWorkflowDetails: 'uninitialized', + errorWorkflowDetails: null, + statusDoWorkflowAction: 'uninitialized', + errorDoWorkflowAction: null, + statusDeleteWorkflow: 'uninitialized', + errorDeleteWorkflow: null, + statusWorkflowOperations: 'uninitialized', + errorWorkflowOperations: null, + statusWorkflowOperationDetails: 'uninitialized', + errorWorkflowOperationDetails: null, + statusWorkflowErrors: 'uninitialized', + errorWorkflowErrors: null, + statusWorkflowErrorDetails: 'uninitialized', + errorWorkflowErrorDetails: null, + statusStatistics: 'uninitialized', + errorStatistics: null, + statusStatisticsValue: 'uninitialized', + errorStatisticsValue: null, + statusTobiraData: 'uninitialized', + errorTobiraData: null, + eventId: "", + modal: { + show: false, + page: EventDetailsPage.Metadata, + event: null, + workflowTabHierarchy: 'entry', + assetsTabHierarchy: 'entry', + workflowId: "", + }, + metadata: { + title: "", + flavor: "", + fields: [], + }, + extendedMetadata: [], + assets: { + attachments: 0, + catalogs: 0, + media: 0, + publications: 0, + }, + transactionsReadOnly: false, + uploadAssetOptions: [], + assetAttachments: [], + assetAttachmentDetails: { + id: "", + type: "", + mimetype: "", + size: 0, + checksum: undefined, + reference: "", + tags: [], + url: "", + }, + assetCatalogs: [], + assetCatalogDetails: { + id: "", + type: "", + mimetype: "", + size: 0, + checksum: undefined, + reference: "", + tags: [], + url: "", + }, + assetMedia: [], + assetMediaDetails: { + id: "", + type: "", + mimetype: "", + tags: [], + duration: 0, + size: 0, + checksum: undefined, + reference: "", + has_audio: false, + has_subtitle: false, + has_video: false, + url: "", + streams: { + audio: [], + video: [], + }, + video: undefined, + }, + assetPublications: [], + assetPublicationDetails: { + id: "", + type: "", + mimetype: "", + size: 0, + channel: "", + checksum: undefined, + reference: "", + tags: [], + url: "", + }, + policies: [], + comments: [], + commentReasons: {}, + scheduling: { + hasProperties: false, + }, + schedulingSource: { + start: { + date: "", + hour: undefined, + minute: undefined, + }, + duration: { + hour: undefined, + minute: undefined, + }, + end: { + date: "", + hour: undefined, + minute: undefined, + }, + device: { + id: "", + name: "", + inputs: [], + inputMethods: [], + }, + agentId: undefined, + agentConfiguration: {}, + }, + hasSchedulingConflicts: false, + schedulingConflicts: [], + workflows: { + scheduling: false, + entries: [], + workflow: { + workflowId: "", + description: "", + }, + }, + workflowConfiguration: { + workflowId: "", + description: "", + }, + workflowDefinitions: [], + baseWorkflow: { + workflowId: "" + }, + workflowOperations: { + entries: [], + }, + workflowOperationDetails: { + completed: "", + description: "", + exception_handler_workflow: "", + execution_host: "", + fail_on_error: false, + failed_attempts: 0, + job: 0, + max_attempts: 0, + name: "", + retry_strategy: "", + started: "", + state: "", + time_in_queue: 0, + }, + workflowErrors: { + entries: [] + }, + workflowErrorDetails: { + description: "", + details: [], + id: 0, + job_id: 0, + processing_host: "", + service_type: "", + severity: "", + technical_details: "", + timestamp: "", + title: "", + }, + publications: [], + statistics: [], + hasStatisticsError: false, + tobiraData: { + baseURL: "", + hostPages: [], + }, }; export const fetchMetadata = createAppAsyncThunk('eventDetails/fetchMetadata', async (eventId: Event["id"]) => { - const metadataRequest = await axios.get(`/admin-ng/event/${eventId}/metadata.json`); - const metadataResponse = await metadataRequest.data; - - const mainCatalog = "dublincore/episode"; - let metadata: MetadataCatalog = { - title: "", - flavor: "", - fields: [] - }; - let extendedMetadata = []; - - for (const catalog of metadataResponse) { - let transformedCatalog = { ...catalog }; - - if (catalog.locked !== undefined) { - let fields = []; - - for (const field of catalog.fields) { - const adaptedField = { - ...field, - locked: catalog.locked, - readOnly: true, - }; - - fields.push(adaptedField); - } - transformedCatalog = { - ...catalog, - fields: fields, - }; - } - if (catalog.flavor === mainCatalog) { - metadata = transformMetadataCollection({ ...transformedCatalog }); - } else { - extendedMetadata.push( - transformMetadataCollection({ ...transformedCatalog }) - ); - } - } - - return { metadata, extendedMetadata } + const metadataRequest = await axios.get(`/admin-ng/event/${eventId}/metadata.json`); + const metadataResponse = await metadataRequest.data; + + const mainCatalog = "dublincore/episode"; + let metadata: MetadataCatalog = { + title: "", + flavor: "", + fields: [] + }; + let extendedMetadata = []; + + for (const catalog of metadataResponse) { + let transformedCatalog = { ...catalog }; + + if (catalog.locked !== undefined) { + let fields = []; + + for (const field of catalog.fields) { + const adaptedField = { + ...field, + locked: catalog.locked, + readOnly: true, + }; + + fields.push(adaptedField); + } + transformedCatalog = { + ...catalog, + fields: fields, + }; + } + if (catalog.flavor === mainCatalog) { + metadata = transformMetadataCollection({ ...transformedCatalog }); + } else { + extendedMetadata.push( + transformMetadataCollection({ ...transformedCatalog }) + ); + } + } + + return { metadata, extendedMetadata } }); export const fetchAssets = createAppAsyncThunk('eventDetails/fetchAssets', async (eventId: Event["id"], { dispatch }) => { - const assetsRequest = await axios.get( - `/admin-ng/event/${eventId}/asset/assets.json` - ); - const assets = await assetsRequest.data; - - let transactionsReadOnly = true; - const fetchTransactionResult = await dispatch(fetchHasActiveTransactions(eventId)) - .then(unwrapResult); - if (fetchTransactionResult.active !== undefined) { - transactionsReadOnly = fetchTransactionResult.active; - } - - const resourceOptionsListRequest = await axios.get( - `/admin-ng/resources/eventUploadAssetOptions.json` - ); - const resourceOptionsListResponse = await resourceOptionsListRequest.data; - - let uploadAssetOptions: UploadAssetOption[] | undefined = []; - const optionsData = formatUploadAssetOptions(resourceOptionsListResponse); - - for (const option of optionsData.options) { - if (option.type !== "track") { - uploadAssetOptions.push({ ...option }); - } - } - - // if no asset options, undefine the option variable - uploadAssetOptions = - uploadAssetOptions.length > 0 ? uploadAssetOptions : undefined; - - return { assets, transactionsReadOnly, uploadAssetOptions } + const assetsRequest = await axios.get( + `/admin-ng/event/${eventId}/asset/assets.json` + ); + const assets = await assetsRequest.data; + + let transactionsReadOnly = true; + const fetchTransactionResult = await dispatch(fetchHasActiveTransactions(eventId)) + .then(unwrapResult); + if (fetchTransactionResult.active !== undefined) { + transactionsReadOnly = fetchTransactionResult.active; + } + + const resourceOptionsListRequest = await axios.get( + `/admin-ng/resources/eventUploadAssetOptions.json` + ); + const resourceOptionsListResponse = await resourceOptionsListRequest.data; + + let uploadAssetOptions: UploadAssetOption[] | undefined = []; + const optionsData = formatUploadAssetOptions(resourceOptionsListResponse); + + for (const option of optionsData.options) { + if (option.type !== "track") { + uploadAssetOptions.push({ ...option }); + } + } + + // if no asset options, undefine the option variable + uploadAssetOptions = + uploadAssetOptions.length > 0 ? uploadAssetOptions : undefined; + + return { assets, transactionsReadOnly, uploadAssetOptions } }); const formatUploadAssetOptions = (optionsData: { [key: string]: string }) => { - const optionPrefixSource = "EVENTS.EVENTS.NEW.SOURCE.UPLOAD"; - const optionPrefixAsset = "EVENTS.EVENTS.NEW.UPLOAD_ASSET.OPTION"; - const workflowPrefix = "EVENTS.EVENTS.NEW.UPLOAD_ASSET.WORKFLOWDEFID"; - - let optionsResult: { - workflow?: string, - options: UploadAssetOption[], - } = { - options: [] - }; - let uploadOptions = []; - - for (const [key, value] of Object.entries(optionsData)) { - if (key.charAt(0) !== "$") { - if ( - key.indexOf(optionPrefixAsset) >= 0 || - key.indexOf(optionPrefixSource) >= 0 - ) { - // parse upload asset options - let options: UploadAssetOption = JSON.parse(value); - if (!options["title"]) { - options["title"] = key; - } - uploadOptions.push({ ...options }); - } else if (key.indexOf(workflowPrefix) >= 0) { - // parse upload workflow definition id - optionsResult["workflow"] = value; - } - } - } - optionsResult["options"] = uploadOptions; - - return optionsResult; + const optionPrefixSource = "EVENTS.EVENTS.NEW.SOURCE.UPLOAD"; + const optionPrefixAsset = "EVENTS.EVENTS.NEW.UPLOAD_ASSET.OPTION"; + const workflowPrefix = "EVENTS.EVENTS.NEW.UPLOAD_ASSET.WORKFLOWDEFID"; + + let optionsResult: { + workflow?: string, + options: UploadAssetOption[], + } = { + options: [] + }; + let uploadOptions = []; + + for (const [key, value] of Object.entries(optionsData)) { + if (key.charAt(0) !== "$") { + if ( + key.indexOf(optionPrefixAsset) >= 0 || + key.indexOf(optionPrefixSource) >= 0 + ) { + // parse upload asset options + let options: UploadAssetOption = JSON.parse(value); + if (!options["title"]) { + options["title"] = key; + } + uploadOptions.push({ ...options }); + } else if (key.indexOf(workflowPrefix) >= 0) { + // parse upload workflow definition id + optionsResult["workflow"] = value; + } + } + } + optionsResult["options"] = uploadOptions; + + return optionsResult; }; export const fetchAssetAttachments = createAppAsyncThunk('eventDetails/fetchAssetAttachments', async (eventId: Event["id"]) => { - let params = new URLSearchParams(); - params.append("id1", "attachment"); - - const attachmentsRequest = await axios.get( - `/admin-ng/event/${eventId}/asset/attachment/attachments.json`, - { params } - ); - return await attachmentsRequest.data; + let params = new URLSearchParams(); + params.append("id1", "attachment"); + + const attachmentsRequest = await axios.get( + `/admin-ng/event/${eventId}/asset/attachment/attachments.json`, + { params } + ); + return await attachmentsRequest.data; }); export const fetchAssetAttachmentDetails = createAppAsyncThunk('eventDetails/fetchAssetAttachmentDetails', async (params: { - eventId: Event["id"], - attachmentId: EventDetailsState["assetAttachments"][0]["id"] + eventId: Event["id"], + attachmentId: EventDetailsState["assetAttachments"][0]["id"] }) => { - const { eventId, attachmentId } = params; - let searchParams = new URLSearchParams(); - searchParams.append("id1", "attachment"); - - const attachmentDetailsRequest = await axios.get( - `/admin-ng/event/${eventId}/asset/attachment/${attachmentId}.json`, - { params } - ); - return await attachmentDetailsRequest.data; + const { eventId, attachmentId } = params; + let searchParams = new URLSearchParams(); + searchParams.append("id1", "attachment"); + + const attachmentDetailsRequest = await axios.get( + `/admin-ng/event/${eventId}/asset/attachment/${attachmentId}.json`, + { params } + ); + return await attachmentDetailsRequest.data; }); export const fetchAssetCatalogs = createAppAsyncThunk('eventDetails/fetchAssetCatalogs', async (eventId: Event["id"]) => { - let params = new URLSearchParams(); - params.append("id1", "catalog"); - - const catalogsRequest = await axios.get( - `/admin-ng/event/${eventId}/asset/catalog/catalogs.json`, - { params } - ); - return await catalogsRequest.data; + let params = new URLSearchParams(); + params.append("id1", "catalog"); + + const catalogsRequest = await axios.get( + `/admin-ng/event/${eventId}/asset/catalog/catalogs.json`, + { params } + ); + return await catalogsRequest.data; }); export const fetchAssetCatalogDetails = createAppAsyncThunk('eventDetails/fetchAssetCatalogDetails', async (params: { - eventId: Event["id"], - catalogId: EventDetailsState["assetCatalogs"][0]["id"] + eventId: Event["id"], + catalogId: EventDetailsState["assetCatalogs"][0]["id"] }) => { - const { eventId, catalogId } = params; - let searchParams = new URLSearchParams(); - searchParams.append("id1", "catalog"); - - const catalogDetailsRequest = await axios.get( - `/admin-ng/event/${eventId}/asset/catalog/${catalogId}.json`, - { params } - ); - return await catalogDetailsRequest.data; + const { eventId, catalogId } = params; + let searchParams = new URLSearchParams(); + searchParams.append("id1", "catalog"); + + const catalogDetailsRequest = await axios.get( + `/admin-ng/event/${eventId}/asset/catalog/${catalogId}.json`, + { params } + ); + return await catalogDetailsRequest.data; }); export const fetchAssetMedia = createAppAsyncThunk('eventDetails/fetchAssetMedia', async (eventId: Event["id"]) => { - let params = new URLSearchParams(); - params.append("id1", "media"); - - const mediaRequest = await axios.get( - `/admin-ng/event/${eventId}/asset/media/media.json`, - { params } - ); - const mediaResponse = await mediaRequest.data; - - let media = []; - - //for every media file item we define the filename - for (let i = 0; i < mediaResponse.length; i++) { - let item = mediaResponse[i]; - const url = item.url; - item.mediaFileName = url - .substring(url.lastIndexOf("/") + 1) - .split("?")[0]; - media.push(item); - } - - return media; + let params = new URLSearchParams(); + params.append("id1", "media"); + + const mediaRequest = await axios.get( + `/admin-ng/event/${eventId}/asset/media/media.json`, + { params } + ); + const mediaResponse = await mediaRequest.data; + + let media = []; + + //for every media file item we define the filename + for (let i = 0; i < mediaResponse.length; i++) { + let item = mediaResponse[i]; + const url = item.url; + item.mediaFileName = url + .substring(url.lastIndexOf("/") + 1) + .split("?")[0]; + media.push(item); + } + + return media; }); export const fetchAssetMediaDetails = createAppAsyncThunk('eventDetails/fetchAssetMediaDetails', async (params: { - eventId: Event["id"], - mediaId: EventDetailsState["assetMedia"][0]["id"] + eventId: Event["id"], + mediaId: EventDetailsState["assetMedia"][0]["id"] }) => { - const { eventId, mediaId } = params; - let searchParams = new URLSearchParams(); - searchParams.append("id1", "media"); - - const mediaDetailsRequest = await axios.get( - `/admin-ng/event/${eventId}/asset/media/${mediaId}.json`, - { params } - ); - const mediaDetailsResponse = await mediaDetailsRequest.data; - - let mediaDetails; - - if (typeof mediaDetailsResponse === "string") { - mediaDetails = JSON.parse(mediaDetailsResponse); - } else { - mediaDetails = mediaDetailsResponse; - } - - mediaDetails.video = { - video: { - previews: [{ uri: mediaDetails.url }], - }, - url: mediaDetails.url.split("?")[0], - }; - - return mediaDetails; + const { eventId, mediaId } = params; + let searchParams = new URLSearchParams(); + searchParams.append("id1", "media"); + + const mediaDetailsRequest = await axios.get( + `/admin-ng/event/${eventId}/asset/media/${mediaId}.json`, + { params } + ); + const mediaDetailsResponse = await mediaDetailsRequest.data; + + let mediaDetails; + + if (typeof mediaDetailsResponse === "string") { + mediaDetails = JSON.parse(mediaDetailsResponse); + } else { + mediaDetails = mediaDetailsResponse; + } + + mediaDetails.video = { + video: { + previews: [{ uri: mediaDetails.url }], + }, + url: mediaDetails.url.split("?")[0], + }; + + return mediaDetails; }); export const fetchAssetPublications = createAppAsyncThunk('eventDetails/fetchAssetPublications', async (eventId: Event["id"]) => { - let params = new URLSearchParams(); - params.append("id1", "publication"); - - const publicationsRequest = await axios.get( - `/admin-ng/event/${eventId}/asset/publication/publications.json`, - { params } - ); - return await publicationsRequest.data; + let params = new URLSearchParams(); + params.append("id1", "publication"); + + const publicationsRequest = await axios.get( + `/admin-ng/event/${eventId}/asset/publication/publications.json`, + { params } + ); + return await publicationsRequest.data; }); export const fetchAssetPublicationDetails = createAppAsyncThunk('eventDetails/fetchAssetPublicationDetails', async (params: { - eventId: Event["id"], - publicationId: EventDetailsState["publications"][0]["id"] + eventId: Event["id"], + publicationId: EventDetailsState["publications"][0]["id"] }) => { - const { eventId, publicationId } = params; - let searchParams = new URLSearchParams(); - searchParams.append("id1", "publication"); - - const publicationDetailsRequest = await axios.get( - `/admin-ng/event/${eventId}/asset/publication/${publicationId}.json`, - { params } - ); - return await publicationDetailsRequest.data; + const { eventId, publicationId } = params; + let searchParams = new URLSearchParams(); + searchParams.append("id1", "publication"); + + const publicationDetailsRequest = await axios.get( + `/admin-ng/event/${eventId}/asset/publication/${publicationId}.json`, + { params } + ); + return await publicationDetailsRequest.data; }); export const fetchAccessPolicies = createAppAsyncThunk('eventDetails/fetchAccessPolicies', async (id: Event["id"]) => { - const policyData = await axios.get( - `/admin-ng/event/${id}/access.json` - ); - let accessPolicies = await policyData.data; - - let policies: TransformedAcl[] = []; - - if (!accessPolicies.episode_access) { - return policies; - } - - const json = JSON.parse(accessPolicies.episode_access.acl).acl?.ace; - if (json === undefined) { - return policies; - } - - let newPolicies: { [key: string]: TransformedAcl } = {}; - let policyRoles: string[] = []; - - for (let i = 0; i < json.length; i++) { - const policy: Ace = json[i]; - // By default, allow is true - policy.allow ??= true; - if (!newPolicies[policy.role]) { - newPolicies[policy.role] = createPolicy(policy.role); - policyRoles.push(policy.role); - } - if (policy.action === "read" || policy.action === "write") { - newPolicies[policy.role][policy.action] = policy.allow; - } else if (policy.allow) { - newPolicies[policy.role].actions.push(policy.action); - } - } - - policies = policyRoles.map((role) => newPolicies[role]); - - return policies; + const policyData = await axios.get( + `/admin-ng/event/${id}/access.json` + ); + let accessPolicies = await policyData.data; + + let policies: TransformedAcl[] = []; + + if (!accessPolicies.episode_access) { + return policies; + } + + const json = JSON.parse(accessPolicies.episode_access.acl).acl?.ace; + if (json === undefined) { + return policies; + } + + let newPolicies: { [key: string]: TransformedAcl } = {}; + let policyRoles: string[] = []; + + for (let i = 0; i < json.length; i++) { + const policy: Ace = json[i]; + // By default, allow is true + policy.allow ??= true; + if (!newPolicies[policy.role]) { + newPolicies[policy.role] = createPolicy(policy.role); + policyRoles.push(policy.role); + } + if (policy.action === "read" || policy.action === "write") { + newPolicies[policy.role][policy.action] = policy.allow; + } else if (policy.allow) { + newPolicies[policy.role].actions.push(policy.action); + } + } + + policies = policyRoles.map((role) => newPolicies[role]); + + return policies; }); export const fetchComments = createAppAsyncThunk('eventDetails/fetchComments', async (eventId: Event["id"]) => { - const commentsData = await axios.get(`/admin-ng/event/${eventId}/comments`); - const comments = await commentsData.data; + const commentsData = await axios.get(`/admin-ng/event/${eventId}/comments`); + const comments = await commentsData.data; - const commentReasonsData = await axios.get( - `/admin-ng/resources/components.json` - ); - const commentReasons = (await commentReasonsData.data).eventCommentReasons; + const commentReasonsData = await axios.get( + `/admin-ng/resources/components.json` + ); + const commentReasons = (await commentReasonsData.data).eventCommentReasons; - return { comments, commentReasons } + return { comments, commentReasons } }); export const fetchEventPublications = createAppAsyncThunk('eventDetails/fetchEventPublications', async (eventId: Event["id"], { dispatch }) => { - let data = await axios.get(`/admin-ng/event/${eventId}/publications.json`); - - let publications: { - publications: { - id: string, - name: string, - url: string, - }[], - "start-date": string, - "end-date": string, - } = await data.data; - - return await dispatch(enrichPublications(publications)).unwrap(); + let data = await axios.get(`/admin-ng/event/${eventId}/publications.json`); + + let publications: { + publications: { + id: string, + name: string, + url: string, + }[], + "start-date": string, + "end-date": string, + } = await data.data; + + return await dispatch(enrichPublications(publications)).unwrap(); }); // fetch Tobira data of certain series from server export const fetchEventDetailsTobira = createAppAsyncThunk('eventDetails/fetchEventDetailsTobira', async ( - id: string, - { dispatch }, + id: string, + { dispatch }, ) => { - const res = await axios.get(`/admin-ng/event/${id}/tobira/pages`) - .catch(response => handleTobiraError(response, dispatch)); + const res = await axios.get(`/admin-ng/event/${id}/tobira/pages`) + .catch(response => handleTobiraError(response, dispatch)); - if (!res) { - throw Error; - } + if (!res) { + throw Error; + } - const data = res.data; - return data; + const data = res.data; + return data; }); export const saveComment = createAppAsyncThunk('eventDetails/saveComment', async (params: { - eventId: Event["id"], - commentText: Comment["text"], - commentReason: Comment["reason"] + eventId: Event["id"], + commentText: Comment["text"], + commentReason: Comment["reason"] }) => { - const { eventId, commentText, commentReason } = params; - let headers = getHttpHeaders(); + const { eventId, commentText, commentReason } = params; + let headers = getHttpHeaders(); - let data = new URLSearchParams(); - data.append("text", commentText); - data.append("reason", commentReason); + let data = new URLSearchParams(); + data.append("text", commentText); + data.append("reason", commentReason); - const commentSaved = await axios.post( - `/admin-ng/event/${eventId}/comment`, - data.toString(), - headers - ); - await commentSaved.data; + const commentSaved = await axios.post( + `/admin-ng/event/${eventId}/comment`, + data.toString(), + headers + ); + await commentSaved.data; - return true; + return true; }); export const saveCommentReply = createAppAsyncThunk('eventDetails/saveCommentReply', async (params: { - eventId: Event["id"], - commentId: Comment["id"], - replyText: CommentReply["text"], - commentResolved: Comment["resolvedStatus"] + eventId: Event["id"], + commentId: Comment["id"], + replyText: CommentReply["text"], + commentResolved: Comment["resolvedStatus"] }) => { - const { eventId, commentId, replyText, commentResolved } = params; - let headers = getHttpHeaders(); + const { eventId, commentId, replyText, commentResolved } = params; + let headers = getHttpHeaders(); - let data = new URLSearchParams(); - data.append("text", replyText); - data.append("resolved", String(commentResolved)); + let data = new URLSearchParams(); + data.append("text", replyText); + data.append("resolved", String(commentResolved)); - const commentReply = await axios.post( - `/admin-ng/event/${eventId}/comment/${commentId}/reply`, - data.toString(), - headers - ); + const commentReply = await axios.post( + `/admin-ng/event/${eventId}/comment/${commentId}/reply`, + data.toString(), + headers + ); - await commentReply.data; + await commentReply.data; - return true; + return true; }); export const fetchSchedulingInfo = createAppAsyncThunk('eventDetails/fetchSchedulingInfo', async (eventId: Event["id"], { dispatch, getState }) => { - // get data from API about event scheduling - const schedulingRequest = await axios.get( - `/admin-ng/event/${eventId}/scheduling.json` - ); - const schedulingResponse = await schedulingRequest.data; - - // get data from API about capture agents - await dispatch(fetchRecordings("inputs")); - - const state = getState(); - const captureAgents = getRecordings(state); - - const startDate = new Date(schedulingResponse.start); - const endDate = new Date(schedulingResponse.end); - const { durationHours, durationMinutes } = calculateDuration( - startDate, - endDate - ); - - let device: Device = { - id: "", - name: "", - inputs: [], - inputMethods: [], - }; - - const agent = captureAgents.find( - (agent) => agent.id === schedulingResponse.agentId - ); - if (!!agent) { - let inputMethods = []; - - if ( - schedulingResponse.agentConfiguration["capture.device.names"] !== - undefined - ) { - const inputs = schedulingResponse.agentConfiguration[ - "capture.device.names" - ].split(","); - for (const input of inputs) { - inputMethods.push(input); - } - } - device = { - ...agent, - inputMethods: inputMethods, - }; - } - - const source = { - ...schedulingResponse, - start: { - date: schedulingResponse.start, - hour: startDate.getHours(), - minute: startDate.getMinutes(), - }, - end: { - date: schedulingResponse.end, - hour: endDate.getHours(), - minute: endDate.getMinutes(), - }, - duration: { - hour: durationHours, - minute: durationMinutes, - }, - presenters: schedulingResponse.presenters.join(", "), - device: { ...device }, - }; - - return source; + // get data from API about event scheduling + const schedulingRequest = await axios.get( + `/admin-ng/event/${eventId}/scheduling.json` + ); + const schedulingResponse = await schedulingRequest.data; + + // get data from API about capture agents + await dispatch(fetchRecordings("inputs")); + + const state = getState(); + const captureAgents = getRecordings(state); + + const startDate = new Date(schedulingResponse.start); + const endDate = new Date(schedulingResponse.end); + const { durationHours, durationMinutes } = calculateDuration( + startDate, + endDate + ); + + let device: Device = { + id: "", + name: "", + inputs: [], + inputMethods: [], + }; + + const agent = captureAgents.find( + (agent) => agent.id === schedulingResponse.agentId + ); + if (!!agent) { + let inputMethods = []; + + if ( + schedulingResponse.agentConfiguration["capture.device.names"] !== + undefined + ) { + const inputs = schedulingResponse.agentConfiguration[ + "capture.device.names" + ].split(","); + for (const input of inputs) { + inputMethods.push(input); + } + } + device = { + ...agent, + inputMethods: inputMethods, + }; + } + + const source = { + ...schedulingResponse, + start: { + date: schedulingResponse.start, + hour: startDate.getHours(), + minute: startDate.getMinutes(), + }, + end: { + date: schedulingResponse.end, + hour: endDate.getHours(), + minute: endDate.getMinutes(), + }, + duration: { + hour: durationHours, + minute: durationMinutes, + }, + presenters: schedulingResponse.presenters.join(", "), + device: { ...device }, + }; + + return source; }); export type SchedulingInfo = { - captureAgent: string, - inputs: string[], - scheduleDurationHours: string, - scheduleDurationMinutes: string, - scheduleEndDate: string, - scheduleEndHour: string, - scheduleEndMinute: string, - scheduleStartDate: string, - scheduleStartHour: string, - scheduleStartMinute: string, + captureAgent: string, + inputs: string[], + scheduleDurationHours: string, + scheduleDurationMinutes: string, + scheduleEndDate: string, + scheduleEndHour: string, + scheduleEndMinute: string, + scheduleStartDate: string, + scheduleStartHour: string, + scheduleStartMinute: string, } export const saveSchedulingInfo = createAppAsyncThunk('eventDetails/saveSchedulingInfo', async (params: { - eventId: Event["id"], - values: SchedulingInfo, - startDate: Date, - endDate: Date + eventId: Event["id"], + values: SchedulingInfo, + startDate: Date, + endDate: Date }, { dispatch, getState }) => { - const { eventId, values, startDate, endDate } = params; - - const state = getState(); - const oldSource = getSchedulingSource(state); - const captureAgents = getRecordings(state); - let device: Device = { - id: "", - name: "", - inputs: [], - inputMethods: [], - }; - - const agent = captureAgents.find((agent) => agent.id === values.captureAgent); - if (!!agent) { - device = { - ...agent, - inputMethods: values.inputs, - }; - } - - const source = { - ...oldSource, - agentId: device.id, - start: { - date: startDate.toISOString(), - hour: parseInt(values.scheduleStartHour), - minute: parseInt(values.scheduleStartMinute), - }, - end: { - date: endDate.toISOString(), - hour: parseInt(values.scheduleEndHour), - minute: parseInt(values.scheduleEndMinute), - }, - duration: { - hour: parseInt(values.scheduleDurationHours), - minute: parseInt(values.scheduleDurationMinutes), - }, - device: { ...device }, - agentConfiguration: { - ...oldSource.agentConfiguration, - "capture.device.names": values.inputs.join(","), - "event.location": device.id, - }, - }; - - const start = startDate.toISOString(); - const end = endDate.toISOString(); - - const headers = getHttpHeaders(); - let data = new URLSearchParams(); - data.append( - "scheduling", - JSON.stringify({ - agentId: source.agentId, - start: start, - end: end, - agentConfiguration: source.agentConfiguration, - }) - ); - - // save new scheduling information - await axios - .put(`/admin-ng/event/${eventId}/scheduling`, data, headers) - .then((response) => { - dispatch(removeNotificationWizardForm()); - dispatch(fetchSchedulingInfo(eventId)); - }) - .catch((response) => { - dispatch( - addNotification({ - type: "error", - key: "EVENTS_NOT_UPDATED", - parameter: undefined, - context: NOTIFICATION_CONTEXT - }) - ); - throw (response); - }); - - return source; + const { eventId, values, startDate, endDate } = params; + + const state = getState(); + const oldSource = getSchedulingSource(state); + const captureAgents = getRecordings(state); + let device: Device = { + id: "", + name: "", + inputs: [], + inputMethods: [], + }; + + const agent = captureAgents.find((agent) => agent.id === values.captureAgent); + if (!!agent) { + device = { + ...agent, + inputMethods: values.inputs, + }; + } + + const source = { + ...oldSource, + agentId: device.id, + start: { + date: startDate.toISOString(), + hour: parseInt(values.scheduleStartHour), + minute: parseInt(values.scheduleStartMinute), + }, + end: { + date: endDate.toISOString(), + hour: parseInt(values.scheduleEndHour), + minute: parseInt(values.scheduleEndMinute), + }, + duration: { + hour: parseInt(values.scheduleDurationHours), + minute: parseInt(values.scheduleDurationMinutes), + }, + device: { ...device }, + agentConfiguration: { + ...oldSource.agentConfiguration, + "capture.device.names": values.inputs.join(","), + "event.location": device.id, + }, + }; + + const start = startDate.toISOString(); + const end = endDate.toISOString(); + + const headers = getHttpHeaders(); + let data = new URLSearchParams(); + data.append( + "scheduling", + JSON.stringify({ + agentId: source.agentId, + start: start, + end: end, + agentConfiguration: source.agentConfiguration, + }) + ); + + // save new scheduling information + await axios + .put(`/admin-ng/event/${eventId}/scheduling`, data, headers) + .then((response) => { + dispatch(removeNotificationWizardForm()); + dispatch(fetchSchedulingInfo(eventId)); + }) + .catch((response) => { + dispatch( + addNotification({ + type: "error", + key: "EVENTS_NOT_UPDATED", + parameter: undefined, + context: NOTIFICATION_CONTEXT + }) + ); + throw (response); + }); + + return source; }); // TODO: This does not return a boolean anymore. Fix this in usage, make users // get their info from the state export const checkConflicts = createAppAsyncThunk('eventDetails/checkConflicts', async (params: { - eventId: Event["id"], - startDate: Date, - endDate: Date, - deviceId: EventDetailsState["schedulingSource"]["device"]["id"] + eventId: Event["id"], + startDate: Date, + endDate: Date, + deviceId: EventDetailsState["schedulingSource"]["device"]["id"] }, { dispatch }) => { const { eventId, startDate, endDate, deviceId } = params; const conflicts: EventDetailsState["schedulingConflicts"] = []; @@ -1177,253 +1177,253 @@ let hasSchedulingConflicts = false; const now = new Date(); if (endDate < now) { - dispatch(removeNotificationWizardForm()); - dispatch( - addNotification({ - type: "error", - key: "CONFLICT_IN_THE_PAST", - duration: -1, - parameter: undefined, - context: NOTIFICATION_CONTEXT - }) - ); - hasSchedulingConflicts = true; + dispatch(removeNotificationWizardForm()); + dispatch( + addNotification({ + type: "error", + key: "CONFLICT_IN_THE_PAST", + duration: -1, + parameter: undefined, + context: NOTIFICATION_CONTEXT + }) + ); + hasSchedulingConflicts = true; } else { - dispatch(removeNotificationWizardForm()); - let headers = getHttpHeaders(); - - const conflictTimeFrame = { - id: eventId, - start: startDate.toISOString(), - duration: endDate.getTime() - startDate.getTime(), - device: deviceId, - end: endDate.toISOString(), - }; - - let data = new URLSearchParams(); - data.append("metadata", JSON.stringify(conflictTimeFrame)); - - await axios - .post(`/admin-ng/event/new/conflicts`, data, headers) - .then((response) => { - const responseStatus = response.status; - if (responseStatus === 409) { - //conflict detected, add notification and get conflict specifics - dispatch( - addNotification({ - type: "error", - key: "CONFLICT_DETECTED", - duration:-1, - parameter: undefined, - context: NOTIFICATION_CONTEXT - }) - ); - const conflictsResponse = response.data; - - for (const conflict of conflictsResponse) { - conflicts.push({ - title: conflict.title, - start: conflict.start, - end: conflict.end, - }); - } - - hasSchedulingConflicts = true; - } else if (responseStatus === 204) { - //no conflicts detected - hasSchedulingConflicts = false; - } else { - hasSchedulingConflicts = true; - } - }) - .catch((error) => { - const responseStatus = error.response.status; - if (responseStatus === 409) { - //conflict detected, add notification and get conflict specifics - dispatch( - addNotification({ - type: "error", - key: "CONFLICT_DETECTED", - duration:-1, - parameter: undefined, - context: NOTIFICATION_CONTEXT - }) - ); - const conflictsResponse = error.response.data; - - for (const conflict of conflictsResponse) { - conflicts.push({ - title: conflict.title, - start: conflict.start, - end: conflict.end, - }); - } - - hasSchedulingConflicts = true; - } else { - hasSchedulingConflicts = true; - } - }); - } - - return { conflicts, hasSchedulingConflicts }; + dispatch(removeNotificationWizardForm()); + let headers = getHttpHeaders(); + + const conflictTimeFrame = { + id: eventId, + start: startDate.toISOString(), + duration: endDate.getTime() - startDate.getTime(), + device: deviceId, + end: endDate.toISOString(), + }; + + let data = new URLSearchParams(); + data.append("metadata", JSON.stringify(conflictTimeFrame)); + + await axios + .post(`/admin-ng/event/new/conflicts`, data, headers) + .then((response) => { + const responseStatus = response.status; + if (responseStatus === 409) { + //conflict detected, add notification and get conflict specifics + dispatch( + addNotification({ + type: "error", + key: "CONFLICT_DETECTED", + duration:-1, + parameter: undefined, + context: NOTIFICATION_CONTEXT + }) + ); + const conflictsResponse = response.data; + + for (const conflict of conflictsResponse) { + conflicts.push({ + title: conflict.title, + start: conflict.start, + end: conflict.end, + }); + } + + hasSchedulingConflicts = true; + } else if (responseStatus === 204) { + //no conflicts detected + hasSchedulingConflicts = false; + } else { + hasSchedulingConflicts = true; + } + }) + .catch((error) => { + const responseStatus = error.response.status; + if (responseStatus === 409) { + //conflict detected, add notification and get conflict specifics + dispatch( + addNotification({ + type: "error", + key: "CONFLICT_DETECTED", + duration:-1, + parameter: undefined, + context: NOTIFICATION_CONTEXT + }) + ); + const conflictsResponse = error.response.data; + + for (const conflict of conflictsResponse) { + conflicts.push({ + title: conflict.title, + start: conflict.start, + end: conflict.end, + }); + } + + hasSchedulingConflicts = true; + } else { + hasSchedulingConflicts = true; + } + }); + } + + return { conflicts, hasSchedulingConflicts }; }); export const fetchWorkflows = createAppAsyncThunk('eventDetails/fetchWorkflows', async (eventId: Event["id"], { dispatch, getState }) => { - const data = await axios.get(`/admin-ng/event/${eventId}/workflows.json`); - const workflowsData = await data.data; - let workflows: Workflow; - - if (!!workflowsData.results) { - workflows = { - entries: workflowsData.results, - scheduling: false, - workflow: { - workflowId: "", - description: undefined, - configuration: undefined - }, - }; - - } else { - workflows = { - workflow: { - workflowId: workflowsData.workflowId, - description: undefined, - configuration: workflowsData.configuration, - }, - scheduling: true, - entries: [], - }; - - await dispatch(fetchWorkflowDef("event-details")); - - const state = getState(); - - const workflowDefinitions = getWorkflowDef(state); - - dispatch(setEventWorkflowDefinitions({workflows, workflowDefinitions})); - } - - return workflows; + const data = await axios.get(`/admin-ng/event/${eventId}/workflows.json`); + const workflowsData = await data.data; + let workflows: Workflow; + + if (!!workflowsData.results) { + workflows = { + entries: workflowsData.results, + scheduling: false, + workflow: { + workflowId: "", + description: undefined, + configuration: undefined + }, + }; + + } else { + workflows = { + workflow: { + workflowId: workflowsData.workflowId, + description: undefined, + configuration: workflowsData.configuration, + }, + scheduling: true, + entries: [], + }; + + await dispatch(fetchWorkflowDef("event-details")); + + const state = getState(); + + const workflowDefinitions = getWorkflowDef(state); + + dispatch(setEventWorkflowDefinitions({workflows, workflowDefinitions})); + } + + return workflows; }); export const fetchWorkflowDetails = createAppAsyncThunk('eventDetails/fetchWorkflowDetails', async (params: { - eventId: Event["id"], - workflowId: string + eventId: Event["id"], + workflowId: string }) => { - const { eventId, workflowId } = params; - const data = await axios.get( - `/admin-ng/event/${eventId}/workflows/${workflowId}.json` - ); - return await data.data; + const { eventId, workflowId } = params; + const data = await axios.get( + `/admin-ng/event/${eventId}/workflows/${workflowId}.json` + ); + return await data.data; }); export const performWorkflowAction = createAppAsyncThunk('eventDetails/performWorkflowAction', async (params: { - eventId: Event["id"], - workflowId: string, - action: string, - close?: () => void, + eventId: Event["id"], + workflowId: string, + action: string, + close?: () => void, }, { dispatch }) => { - const { eventId, workflowId, action, close} = params; - let headers = { - headers: { - "Content-Type": "application/json;charset=utf-8", - }, - }; - - let data = { - action: action, - id: eventId, - wfId: workflowId, - }; - - await axios - .put( - `/admin-ng/event/${eventId}/workflows/${workflowId}/action/${action}`, - data, - headers - ) - .then((response) => { - dispatch( - addNotification({ - type: "success", - key: "EVENTS_PROCESSING_ACTION_" + action, - duration: -1, - parameter: undefined, - context: NOTIFICATION_CONTEXT - }) - ); - close && close(); - }) - .catch((response) => { - dispatch( - addNotification({ - type: "error", - key: "EVENTS_PROCESSING_ACTION_NOT_" + action, - duration: -1, - parameter: undefined, - context: NOTIFICATION_CONTEXT - }) - ); - throw (response) - }); + const { eventId, workflowId, action, close} = params; + let headers = { + headers: { + "Content-Type": "application/json;charset=utf-8", + }, + }; + + let data = { + action: action, + id: eventId, + wfId: workflowId, + }; + + await axios + .put( + `/admin-ng/event/${eventId}/workflows/${workflowId}/action/${action}`, + data, + headers + ) + .then((response) => { + dispatch( + addNotification({ + type: "success", + key: "EVENTS_PROCESSING_ACTION_" + action, + duration: -1, + parameter: undefined, + context: NOTIFICATION_CONTEXT + }) + ); + close && close(); + }) + .catch((response) => { + dispatch( + addNotification({ + type: "error", + key: "EVENTS_PROCESSING_ACTION_NOT_" + action, + duration: -1, + parameter: undefined, + context: NOTIFICATION_CONTEXT + }) + ); + throw (response) + }); }); export const deleteWorkflow = createAppAsyncThunk('eventDetails/deleteWorkflow', async (params: { - eventId: Event["id"], - workflowId: string + eventId: Event["id"], + workflowId: string }, { dispatch, getState }) => { - const { eventId, workflowId } = params; - - const workflowEntries = await axios - .delete(`/admin-ng/event/${eventId}/workflows/${workflowId}`) - .then((response) => { - dispatch( - addNotification({ - type: "success", - key: "EVENTS_PROCESSING_DELETE_WORKFLOW", - duration: -1, - parameter: undefined, - context: NOTIFICATION_CONTEXT - }) - ); - - const state = getState(); - const workflows = getWorkflows(state); - - if (!!workflows.entries) { - return workflows.entries.filter((wf) => wf.id !== workflowId) - } else { - return workflows.entries; - } - }) - .catch((response) => { - dispatch( - addNotification({ - type: "error", - key: "EVENTS_PROCESSING_DELETE_WORKFLOW_FAILED", - duration: -1, - parameter: undefined, - context: NOTIFICATION_CONTEXT - }) - ); - throw (response); - }); - - return workflowEntries; + const { eventId, workflowId } = params; + + const workflowEntries = await axios + .delete(`/admin-ng/event/${eventId}/workflows/${workflowId}`) + .then((response) => { + dispatch( + addNotification({ + type: "success", + key: "EVENTS_PROCESSING_DELETE_WORKFLOW", + duration: -1, + parameter: undefined, + context: NOTIFICATION_CONTEXT + }) + ); + + const state = getState(); + const workflows = getWorkflows(state); + + if (!!workflows.entries) { + return workflows.entries.filter((wf) => wf.id !== workflowId) + } else { + return workflows.entries; + } + }) + .catch((response) => { + dispatch( + addNotification({ + type: "error", + key: "EVENTS_PROCESSING_DELETE_WORKFLOW_FAILED", + duration: -1, + parameter: undefined, + context: NOTIFICATION_CONTEXT + }) + ); + throw (response); + }); + + return workflowEntries; }); export const fetchWorkflowOperations = createAppAsyncThunk('eventDetails/fetchWorkflowOperations', async (params: { - eventId: Event["id"], - workflowId: string + eventId: Event["id"], + workflowId: string }) => { - const { eventId, workflowId } = params; - const data = await axios.get( - `/admin-ng/event/${eventId}/workflows/${workflowId}/operations.json` - ); - const workflowOperationsData = await data.data; - return { entries: workflowOperationsData }; + const { eventId, workflowId } = params; + const data = await axios.get( + `/admin-ng/event/${eventId}/workflows/${workflowId}/operations.json` + ); + const workflowOperationsData = await data.data; + return { entries: workflowOperationsData }; }); /** @@ -1436,1084 +1436,1084 @@ export const fetchWorkflowOperations = createAppAsyncThunk('eventDetails/fetchWo * @param workflowId workflow id required for workflow sub tabs */ export const openModal = ( - page: EventDetailsPage, - event: Event, - workflowTab: WorkflowTabHierarchy = 'entry', - assetsTab: AssetTabHierarchy = 'entry', - workflowId: string = '', + page: EventDetailsPage, + event: Event, + workflowTab: WorkflowTabHierarchy = 'entry', + assetsTab: AssetTabHierarchy = 'entry', + workflowId: string = '', ) => (dispatch: AppDispatch) => { - dispatch(setModalEvent(event)); - dispatch(setModalWorkflowId(workflowId)); - dispatch(openModalTab(page, workflowTab, assetsTab)) - dispatch(setShowModal(true)); + dispatch(setModalEvent(event)); + dispatch(setModalWorkflowId(workflowId)); + dispatch(openModalTab(page, workflowTab, assetsTab)) + dispatch(setShowModal(true)); }; export const openModalTab = ( - page: EventDetailsPage, - workflowTab: WorkflowTabHierarchy, - assetsTab: AssetTabHierarchy + page: EventDetailsPage, + workflowTab: WorkflowTabHierarchy, + assetsTab: AssetTabHierarchy ) => (dispatch: AppDispatch) => { - dispatch(setModalPage(page)); - dispatch(setTobiraTabHierarchy("main")); - dispatch(setModalWorkflowTabHierarchy(workflowTab)); - dispatch(setModalAssetsTabHierarchy(assetsTab)); + dispatch(setModalPage(page)); + dispatch(setTobiraTabHierarchy("main")); + dispatch(setModalWorkflowTabHierarchy(workflowTab)); + dispatch(setModalAssetsTabHierarchy(assetsTab)); }; export const fetchWorkflowOperationDetails = createAppAsyncThunk('eventDetails/fetchWorkflowOperationDetails', async (params: { - eventId: Event["id"], - workflowId: string, - operationId?: number + eventId: Event["id"], + workflowId: string, + operationId?: number }) => { - const { eventId, workflowId, operationId } = params; - const data = await axios.get( - `/admin-ng/event/${eventId}/workflows/${workflowId}/operations/${operationId}` - ); - return await data.data; + const { eventId, workflowId, operationId } = params; + const data = await axios.get( + `/admin-ng/event/${eventId}/workflows/${workflowId}/operations/${operationId}` + ); + return await data.data; }); export const fetchWorkflowErrors = createAppAsyncThunk('eventDetails/fetchWorkflowErrors', async (params: { - eventId: Event["id"], - workflowId: string + eventId: Event["id"], + workflowId: string }) => { - const { eventId, workflowId } = params; - const data = await axios.get( - `/admin-ng/event/${eventId}/workflows/${workflowId}/errors.json` - ); - const workflowErrorsData = await data.data; - return { entries: workflowErrorsData }; + const { eventId, workflowId } = params; + const data = await axios.get( + `/admin-ng/event/${eventId}/workflows/${workflowId}/errors.json` + ); + const workflowErrorsData = await data.data; + return { entries: workflowErrorsData }; }); export const fetchWorkflowErrorDetails = createAppAsyncThunk('eventDetails/fetchWorkflowErrorDetails', async (params: { - eventId: Event["id"], - workflowId: number, - errorId?: number + eventId: Event["id"], + workflowId: number, + errorId?: number }) => { - const { eventId, workflowId, errorId } = params; - const data = await axios.get( - `/admin-ng/event/${eventId}/workflows/${workflowId}/errors/${errorId}.json` - ); - return await data.data; + const { eventId, workflowId, errorId } = params; + const data = await axios.get( + `/admin-ng/event/${eventId}/workflows/${workflowId}/errors/${errorId}.json` + ); + return await data.data; }); // TODO: Fix this after the modernization of statisticsThunks happened export const fetchEventStatistics = createAppAsyncThunk('eventDetails/fetchEventStatistics', async (eventId: Event["id"], { getState }) => { - // get prior statistics - const state = getState(); - const statistics = getStatistics(state); - - return await ( - fetchStatistics( - eventId, - "episode", - statistics, - ) - ); + // get prior statistics + const state = getState(); + const statistics = getStatistics(state); + + return await ( + fetchStatistics( + eventId, + "episode", + statistics, + ) + ); }); // TODO: Fix this after the modernization of statisticsThunks happened export const fetchEventStatisticsValueUpdate = createAppAsyncThunk('eventDetails/fetchEventStatisticsValueUpdate', async (params: { - id: Event["id"], - providerId: string, - from: string | Date, - to: string | Date, - dataResolution: DataResolution, - timeMode: TimeMode + id: Event["id"], + providerId: string, + from: string | Date, + to: string | Date, + dataResolution: DataResolution, + timeMode: TimeMode }, { getState }) => { - const { id, providerId, from, to, dataResolution, timeMode } = params; - // get prior statistics - const state = getState(); - const statistics = getStatistics(state); - - return await ( - fetchStatisticsValueUpdate( - id, - "episode", - providerId, - from, - to, - dataResolution, - timeMode, - statistics, - ) - ); + const { id, providerId, from, to, dataResolution, timeMode } = params; + // get prior statistics + const state = getState(); + const statistics = getStatistics(state); + + return await ( + fetchStatisticsValueUpdate( + id, + "episode", + providerId, + from, + to, + dataResolution, + timeMode, + statistics, + ) + ); }); export const updateMetadata = createAppAsyncThunk('eventDetails/updateMetadata', async (params: { - id: Event["id"], - values: { [key: string]: MetadataCatalog["fields"][0]["value"] } - catalog: MetadataCatalog + id: Event["id"], + values: { [key: string]: MetadataCatalog["fields"][0]["value"] } + catalog: MetadataCatalog }, { dispatch, getState }) => { - const { id, values, catalog } = params; - - const { fields, data, headers } = transformMetadataForUpdate( - catalog, - values - ); - - await axios.put(`/admin-ng/event/${id}/metadata`, data, headers); - - // updated metadata in event details redux store - let eventMetadata = { - flavor: catalog.flavor, - title: catalog.title, - fields: fields, - }; - dispatch(setEventMetadata(eventMetadata)); + const { id, values, catalog } = params; + + const { fields, data, headers } = transformMetadataForUpdate( + catalog, + values + ); + + await axios.put(`/admin-ng/event/${id}/metadata`, data, headers); + + // updated metadata in event details redux store + let eventMetadata = { + flavor: catalog.flavor, + title: catalog.title, + fields: fields, + }; + dispatch(setEventMetadata(eventMetadata)); }); export const updateExtendedMetadata = createAppAsyncThunk('eventDetails/updateExtendedMetadata', async (params: { - id: Event["id"], - values: { [key: string]: MetadataCatalog["fields"][0]["value"] } - catalog: MetadataCatalog + id: Event["id"], + values: { [key: string]: MetadataCatalog["fields"][0]["value"] } + catalog: MetadataCatalog }, { dispatch, getState }) => { - const { id, values, catalog } = params; - - const { fields, data, headers } = transformMetadataForUpdate( - catalog, - values - ); - - await axios.put(`/admin-ng/event/${id}/metadata`, data, headers); - - // updated extended metadata in event details redux store - let eventMetadata = { - ...catalog, - fields: fields, - }; - - const oldExtendedMetadata = getExtendedMetadata(getState()); - let newExtendedMetadata = []; - - for (const catalog of oldExtendedMetadata) { - if ( - catalog.flavor === eventMetadata.flavor && - catalog.title === eventMetadata.title - ) { - newExtendedMetadata.push(eventMetadata); - } else { - newExtendedMetadata.push(catalog); - } - } - - dispatch(setExtendedEventMetadata(newExtendedMetadata)); + const { id, values, catalog } = params; + + const { fields, data, headers } = transformMetadataForUpdate( + catalog, + values + ); + + await axios.put(`/admin-ng/event/${id}/metadata`, data, headers); + + // updated extended metadata in event details redux store + let eventMetadata = { + ...catalog, + fields: fields, + }; + + const oldExtendedMetadata = getExtendedMetadata(getState()); + let newExtendedMetadata = []; + + for (const catalog of oldExtendedMetadata) { + if ( + catalog.flavor === eventMetadata.flavor && + catalog.title === eventMetadata.title + ) { + newExtendedMetadata.push(eventMetadata); + } else { + newExtendedMetadata.push(catalog); + } + } + + dispatch(setExtendedEventMetadata(newExtendedMetadata)); }); export const fetchHasActiveTransactions = createAppAsyncThunk('eventDetails/fetchHasActiveTransactions', async (eventId: Event["id"]) => { - const transactionsData = await axios.get( - `/admin-ng/event/${eventId}/hasActiveTransaction` - ); - const hasActiveTransactions = await transactionsData.data; - return hasActiveTransactions; + const transactionsData = await axios.get( + `/admin-ng/event/${eventId}/hasActiveTransaction` + ); + const hasActiveTransactions = await transactionsData.data; + return hasActiveTransactions; }); export const updateAssets = createAppAsyncThunk('eventDetails/updateAssets', async (params: { - values: { [key: string]: File }, - eventId: Event["id"] + values: { [key: string]: File }, + eventId: Event["id"] }, { dispatch, getState }) => { - const { values, eventId } = params; - // get asset upload options from redux store - const state = getState(); - const uploadAssetOptions = getAssetUploadOptions(state); - const uploadAssetWorkflow = getAssetUploadWorkflow(state); - - let formData = new FormData(); - - let assets: { - options: UploadAssetOption[], - } = { - options: [], - }; - - let assetFlavors = ""; - - uploadAssetOptions.forEach((option) => { - if (!!values[option.id]) { - formData.append(option.id + ".0", values[option.id]); - assets.options = assets.options.concat(option); - const uploadAssetFlavor = [option.flavorType, option.flavorSubType].join("/"); - if (assetFlavors.length > 0) { - assetFlavors = [assetFlavors, uploadAssetFlavor].join(","); - } else { - assetFlavors = uploadAssetFlavor; - } - } - }); - - const uploadAssetWorkflowConfiguration: { - "downloadSourceflavorsExist": string, - "download-source-flavors": string, - } = { - "downloadSourceflavorsExist": String(assetFlavors.length > 0), - "download-source-flavors": assetFlavors, - }; - - const processing: { - workflow: string | undefined, - configuration: typeof uploadAssetWorkflowConfiguration, - } = { - workflow: uploadAssetWorkflow, - configuration: uploadAssetWorkflowConfiguration, - }; - - formData.append( - "metadata", - JSON.stringify({ - assets: assets, - processing: processing, - }) - ); - - axios - .post(`/admin-ng/event/${eventId}/assets`, formData, { - headers: { - "Content-Type": "multipart/form-data", - }, - }) - .then((response) => { - console.info(response); - dispatch( - addNotification({ - type: "success", - key: "EVENTS_UPDATED", - parameter: undefined, - context: NOTIFICATION_CONTEXT - }) - ); - }) - .catch((response) => { - console.error(response); - dispatch( - addNotification({ - type: "error", - key: "EVENTS_NOT_UPDATED", - parameter: undefined, - context: NOTIFICATION_CONTEXT - }) - ); - }); + const { values, eventId } = params; + // get asset upload options from redux store + const state = getState(); + const uploadAssetOptions = getAssetUploadOptions(state); + const uploadAssetWorkflow = getAssetUploadWorkflow(state); + + let formData = new FormData(); + + let assets: { + options: UploadAssetOption[], + } = { + options: [], + }; + + let assetFlavors = ""; + + uploadAssetOptions.forEach((option) => { + if (!!values[option.id]) { + formData.append(option.id + ".0", values[option.id]); + assets.options = assets.options.concat(option); + const uploadAssetFlavor = [option.flavorType, option.flavorSubType].join("/"); + if (assetFlavors.length > 0) { + assetFlavors = [assetFlavors, uploadAssetFlavor].join(","); + } else { + assetFlavors = uploadAssetFlavor; + } + } + }); + + const uploadAssetWorkflowConfiguration: { + "downloadSourceflavorsExist": string, + "download-source-flavors": string, + } = { + "downloadSourceflavorsExist": String(assetFlavors.length > 0), + "download-source-flavors": assetFlavors, + }; + + const processing: { + workflow: string | undefined, + configuration: typeof uploadAssetWorkflowConfiguration, + } = { + workflow: uploadAssetWorkflow, + configuration: uploadAssetWorkflowConfiguration, + }; + + formData.append( + "metadata", + JSON.stringify({ + assets: assets, + processing: processing, + }) + ); + + axios + .post(`/admin-ng/event/${eventId}/assets`, formData, { + headers: { + "Content-Type": "multipart/form-data", + }, + }) + .then((response) => { + console.info(response); + dispatch( + addNotification({ + type: "success", + key: "EVENTS_UPDATED", + parameter: undefined, + context: NOTIFICATION_CONTEXT + }) + ); + }) + .catch((response) => { + console.error(response); + dispatch( + addNotification({ + type: "error", + key: "EVENTS_NOT_UPDATED", + parameter: undefined, + context: NOTIFICATION_CONTEXT + }) + ); + }); }); export const saveAccessPolicies = createAppAsyncThunk('eventDetails/saveAccessPolicies', async (params: { - id: Event["id"], - policies: { acl: { ace: Ace[] } } + id: Event["id"], + policies: { acl: { ace: Ace[] } } }, { dispatch }) => { - const { id, policies } = params; - const headers = getHttpHeaders(); - - let data = new URLSearchParams(); - data.append("acl", JSON.stringify(policies)); - data.append("override", "true"); - - return axios - .post(`/admin-ng/event/${id}/access`, data.toString(), headers) - .then((response) => { - console.info(response); - dispatch(fetchAccessPolicies(id)) - dispatch( - addNotification({ - type: "info", - key: "SAVED_ACL_RULES", - duration: -1, - parameter: undefined, - context: NOTIFICATION_CONTEXT - }) - ); - return true; - }) - .catch((response) => { - console.error(response); - dispatch( - addNotification({ - type: "error", - key: "ACL_NOT_SAVED", - duration: -1, - parameter: undefined, - context: NOTIFICATION_CONTEXT - }) - ); - return false; - }); + const { id, policies } = params; + const headers = getHttpHeaders(); + + let data = new URLSearchParams(); + data.append("acl", JSON.stringify(policies)); + data.append("override", "true"); + + return axios + .post(`/admin-ng/event/${id}/access`, data.toString(), headers) + .then((response) => { + console.info(response); + dispatch(fetchAccessPolicies(id)) + dispatch( + addNotification({ + type: "info", + key: "SAVED_ACL_RULES", + duration: -1, + parameter: undefined, + context: NOTIFICATION_CONTEXT + }) + ); + return true; + }) + .catch((response) => { + console.error(response); + dispatch( + addNotification({ + type: "error", + key: "ACL_NOT_SAVED", + duration: -1, + parameter: undefined, + context: NOTIFICATION_CONTEXT + }) + ); + return false; + }); }); export const updateComment = createAppAsyncThunk('eventDetails/updateComment', async (params: { - eventId: Event["id"], - commentId: Comment["id"], - commentText: Comment["text"], - commentReason: Comment["reason"] + eventId: Event["id"], + commentId: Comment["id"], + commentText: Comment["text"], + commentReason: Comment["reason"] }, { dispatch }) => { - const { eventId, commentId, commentText, commentReason } = params; - let headers = getHttpHeaders(); - - let data = new URLSearchParams(); - data.append("text", commentText); - data.append("reason", commentReason); - - const commentUpdated = await axios.put( - `/admin-ng/event/${eventId}/comment/${commentId}`, - data.toString(), - headers - ); - await commentUpdated.data; - return true; + const { eventId, commentId, commentText, commentReason } = params; + let headers = getHttpHeaders(); + + let data = new URLSearchParams(); + data.append("text", commentText); + data.append("reason", commentReason); + + const commentUpdated = await axios.put( + `/admin-ng/event/${eventId}/comment/${commentId}`, + data.toString(), + headers + ); + await commentUpdated.data; + return true; }); export const deleteComment = createAppAsyncThunk('eventDetails/deleteComment', async (params: { - eventId: Event["id"], - commentId: Comment["id"] + eventId: Event["id"], + commentId: Comment["id"] }) => { - const { eventId, commentId } = params; - const commentDeleted = await axios.delete( - `/admin-ng/event/${eventId}/comment/${commentId}` - ); - await commentDeleted.data; - return true; + const { eventId, commentId } = params; + const commentDeleted = await axios.delete( + `/admin-ng/event/${eventId}/comment/${commentId}` + ); + await commentDeleted.data; + return true; }); export const deleteCommentReply = createAppAsyncThunk('eventDetails/deleteCommentReply', async (params: { - eventId: Event["id"], - commentId: Comment["id"], - replyId: CommentReply["id"] + eventId: Event["id"], + commentId: Comment["id"], + replyId: CommentReply["id"] }) => { - const { eventId, commentId, replyId } = params; - const commentReplyDeleted = await axios.delete( - `/admin-ng/event/${eventId}/comment/${commentId}/${replyId}` - ); - await commentReplyDeleted.data; + const { eventId, commentId, replyId } = params; + const commentReplyDeleted = await axios.delete( + `/admin-ng/event/${eventId}/comment/${commentId}/${replyId}` + ); + await commentReplyDeleted.data; - return true; + return true; }); export const saveWorkflowConfig = createAppAsyncThunk('eventDetails/saveWorkflowConfig', async (params: { - values: { - workflowDefinition: string, - configuration: { [key: string]: unknown } | undefined - }, - eventId: Event["id"] + values: { + workflowDefinition: string, + configuration: { [key: string]: unknown } | undefined + }, + eventId: Event["id"] }, { dispatch }) => { - const { values, eventId } = params; - let jsonData = { - id: values.workflowDefinition, - configuration: values.configuration, - }; - - let header = getHttpHeaders(); - let data = new URLSearchParams(); - // Scheduler service in Opencast expects values to be strings, so we convert them here - data.append("configuration", JSON.stringify(jsonData, (k, v) => v && typeof v === 'object' ? v : '' + v)); - - axios - .put(`/admin-ng/event/${eventId}/workflows`, data, header) - .then((response) => { - console.info(response); - dispatch(removeNotificationWizardForm()); - dispatch(fetchWorkflows(eventId)); - }) - .catch((response) => { - console.error(response); - dispatch( - addNotification({ - type: "error", - key: "EVENTS_NOT_UPDATED", - duration: -1, - parameter: undefined, - context: NOTIFICATION_CONTEXT - }) - ); - }); + const { values, eventId } = params; + let jsonData = { + id: values.workflowDefinition, + configuration: values.configuration, + }; + + let header = getHttpHeaders(); + let data = new URLSearchParams(); + // Scheduler service in Opencast expects values to be strings, so we convert them here + data.append("configuration", JSON.stringify(jsonData, (k, v) => v && typeof v === 'object' ? v : '' + v)); + + axios + .put(`/admin-ng/event/${eventId}/workflows`, data, header) + .then((response) => { + console.info(response); + dispatch(removeNotificationWizardForm()); + dispatch(fetchWorkflows(eventId)); + }) + .catch((response) => { + console.error(response); + dispatch( + addNotification({ + type: "error", + key: "EVENTS_NOT_UPDATED", + duration: -1, + parameter: undefined, + context: NOTIFICATION_CONTEXT + }) + ); + }); }); const eventDetailsSlice = createSlice({ - name: 'eventDetails', - initialState, - reducers: { - setShowModal(state, action: PayloadAction< - EventDetailsState["modal"]["show"] - >) { - state.modal.show = action.payload; - }, - setModalPage(state, action: PayloadAction< - EventDetailsState["modal"]["page"] - >) { - state.modal.page = action.payload; - }, - setModalEvent(state, action: PayloadAction< - EventDetailsState["modal"]["event"] - >) { - state.modal.event = action.payload; - }, - setModalWorkflowId(state, action: PayloadAction< - EventDetailsState["modal"]["workflowId"] - >) { - state.modal.workflowId = action.payload; - }, - setModalWorkflowTabHierarchy(state, action: PayloadAction< - EventDetailsState["modal"]["workflowTabHierarchy"] - >) { - state.modal.workflowTabHierarchy = action.payload; - }, - setModalAssetsTabHierarchy(state, action: PayloadAction< - EventDetailsState["modal"]["assetsTabHierarchy"] - >) { - state.modal.assetsTabHierarchy = action.payload; - }, - setEventMetadata(state, action: PayloadAction< - EventDetailsState["metadata"] - >) { - state.metadata = action.payload; - }, - setExtendedEventMetadata(state, action: PayloadAction< - EventDetailsState["extendedMetadata"] - >) { - state.extendedMetadata = action.payload; - }, - setEventWorkflow(state, action: PayloadAction< - EventDetailsState["workflows"]["workflow"] - >) { - state.workflows.workflow = action.payload; - }, - setEventWorkflowDefinitions(state, action: PayloadAction<{ - workflows: EventDetailsState["workflows"], - workflowDefinitions: EventDetailsState["workflowDefinitions"], - }>) { - if ("workflowId" in action.payload.workflows.workflow) { - state.baseWorkflow = { ...action.payload.workflows.workflow }; - } - state.workflows = action.payload.workflows; - state.workflowDefinitions = action.payload.workflowDefinitions; - }, - setEventWorkflowConfiguration(state, action: PayloadAction<{ - workflowConfiguration: EventDetailsState["workflowConfiguration"], - }>) { - state.workflowConfiguration = action.payload.workflowConfiguration; - }, - }, - // These are used for thunks - extraReducers: builder => { - builder - // fetchMetadata - .addCase(fetchMetadata.pending, (state) => { - state.statusMetadata = 'loading'; - }) - .addCase(fetchMetadata.fulfilled, (state, action: PayloadAction<{ - metadata: EventDetailsState["metadata"], - extendedMetadata: EventDetailsState["extendedMetadata"], - }>) => { - state.statusMetadata = 'succeeded'; - const eventDetails = action.payload; - state.metadata = eventDetails.metadata; - state.extendedMetadata = eventDetails.extendedMetadata; - }) - .addCase(fetchMetadata.rejected, (state, action) => { - state.statusMetadata = 'failed'; - state.metadata = { - title: "", - flavor: "", - fields: [], - }; - state.extendedMetadata = []; - state.errorMetadata = action.error; - console.error(action.error); - }) - // fetchAssets - .addCase(fetchAssets.pending, (state) => { - state.statusAssets = 'loading'; - }) - .addCase(fetchAssets.fulfilled, (state, action: PayloadAction<{ - assets: EventDetailsState["assets"], - transactionsReadOnly: EventDetailsState["transactionsReadOnly"], - uploadAssetOptions: EventDetailsState["uploadAssetOptions"], - }>) => { - state.statusAssets = 'succeeded'; - const eventDetails = action.payload; - state.assets = eventDetails.assets; - state.transactionsReadOnly = eventDetails.transactionsReadOnly; - state.uploadAssetOptions = eventDetails.uploadAssetOptions; - }) - .addCase(fetchAssets.rejected, (state, action) => { - state.statusAssets = 'failed'; - const emptyAssets = { - attachments: 0, - catalogs: 0, - media: 0, - publications: 0, - }; - state.assets = emptyAssets; - state.transactionsReadOnly = false; - state.uploadAssetOptions = []; - state.errorAssets = action.error; - console.error(action.error); - }) - // fetchAssetAttachments - .addCase(fetchAssetAttachments.pending, (state) => { - state.statusAssetAttachments = 'loading'; - }) - .addCase(fetchAssetAttachments.fulfilled, (state, action: PayloadAction< - EventDetailsState["assetAttachments"] - >) => { - state.statusAssetAttachments = 'succeeded'; - state.assetAttachments = action.payload; - }) - .addCase(fetchAssetAttachments.rejected, (state, action) => { - state.statusAssetAttachments = 'failed'; - state.assetAttachments = []; - state.errorAssetAttachments = action.error; - console.error(action.error); - }) - // fetchAssetAttachmentDetails - .addCase(fetchAssetAttachmentDetails.pending, (state) => { - state.statusAssetAttachments = 'loading'; - }) - .addCase(fetchAssetAttachmentDetails.fulfilled, (state, action: PayloadAction< - EventDetailsState["assetAttachmentDetails"] - >) => { - state.statusAssetAttachments = 'succeeded'; - state.assetAttachmentDetails = action.payload; - }) - .addCase(fetchAssetAttachmentDetails.rejected, (state, action) => { - state.statusAssetAttachments = 'failed'; - const emptyAssetAttachmentDetails = { - id: "", - type: "", - mimetype: "", - size: 0, - checksum: undefined, - reference: "", - tags: [], - url: "", - }; - state.assetAttachmentDetails = emptyAssetAttachmentDetails; - state.errorAssetAttachments = action.error; - console.error(action.error); - }) - // fetchAssetCatalogs - .addCase(fetchAssetCatalogs.pending, (state) => { - state.statusAssetCatalogs = 'loading'; - }) - .addCase(fetchAssetCatalogs.fulfilled, (state, action: PayloadAction< - EventDetailsState["assetCatalogs"] - >) => { - state.statusAssetCatalogs = 'succeeded'; - state.assetCatalogs = action.payload; - }) - .addCase(fetchAssetCatalogs.rejected, (state, action) => { - state.statusAssetCatalogs = 'failed'; - state.assetCatalogs = []; - state.errorAssetCatalogs = action.error; - console.error(action.error); - }) - // fetchAssetCatalogDetails - .addCase(fetchAssetCatalogDetails.pending, (state) => { - state.statusAssetCatalogDetails = 'loading'; - }) - .addCase(fetchAssetCatalogDetails.fulfilled, (state, action: PayloadAction< - EventDetailsState["assetCatalogDetails"] - >) => { - state.statusAssetCatalogDetails = 'succeeded'; - state.assetCatalogDetails = action.payload; - }) - .addCase(fetchAssetCatalogDetails.rejected, (state, action) => { - state.statusAssetCatalogDetails = 'failed'; - const emptyAssetCatalogDetails = { - id: "", - type: "", - mimetype: "", - size: 0, - checksum: undefined, - reference: "", - tags: [], - url: "", - }; - state.assetCatalogDetails = emptyAssetCatalogDetails; - state.errorAssetCatalogDetails = action.error; - console.error(action.error); - }) - // fetchAssetMedia - .addCase(fetchAssetMedia.pending, (state) => { - state.statusAssetMedia = 'loading'; - }) - .addCase(fetchAssetMedia.fulfilled, (state, action: PayloadAction< - EventDetailsState["assetMedia"] - >) => { - state.statusAssetMedia = 'succeeded'; - state.assetMedia = action.payload; - }) - .addCase(fetchAssetMedia.rejected, (state, action) => { - state.statusAssetMedia = 'failed'; - state.assetMedia = []; - state.errorAssetMedia = action.error; - console.error(action.error); - }) - // fetchAssetMediaDetails - .addCase(fetchAssetMediaDetails.pending, (state) => { - state.statusAssetMediaDetails = 'loading'; - }) - .addCase(fetchAssetMediaDetails.fulfilled, (state, action: PayloadAction< - EventDetailsState["assetMediaDetails"] - >) => { - state.statusAssetMediaDetails = 'succeeded'; - state.assetMediaDetails = action.payload; - }) - .addCase(fetchAssetMediaDetails.rejected, (state, action) => { - state.statusAssetMediaDetails = 'failed'; - const emptyAssetMediaDetails = { - id: "", - type: "", - mimetype: "", - tags: [], - duration: 0, - size: 0, - checksum: undefined, - reference: "", - has_audio: false, - has_subtitle: false, - has_video: false, - url: "", - streams: { - audio: [], - video: [], - }, - video: undefined, - }; - state.assetMediaDetails = emptyAssetMediaDetails; - state.errorAssetMediaDetails = action.error; - console.error(action.error); - }) - // fetchAssetPublications - .addCase(fetchAssetPublications.pending, (state) => { - state.statusAssetPublications = 'loading'; - }) - .addCase(fetchAssetPublications.fulfilled, (state, action: PayloadAction< - EventDetailsState["assetPublications"] - >) => { - state.statusAssetPublications = 'succeeded'; - state.assetPublications = action.payload; - }) - .addCase(fetchAssetPublications.rejected, (state, action) => { - state.statusAssetPublications = 'failed'; - state.assetPublications = []; - state.errorAssetPublications = action.error; - console.error(action.error); - }) - // fetchAssetPublicationDetails - .addCase(fetchAssetPublicationDetails.pending, (state) => { - state.statusAssetPublicationDetails = 'loading'; - }) - .addCase(fetchAssetPublicationDetails.fulfilled, (state, action: PayloadAction< - EventDetailsState["assetPublicationDetails"] - >) => { - state.statusAssetPublicationDetails = 'succeeded'; - state.assetPublicationDetails = action.payload; - }) - .addCase(fetchAssetPublicationDetails.rejected, (state, action) => { - state.statusAssetPublicationDetails = 'failed'; - const emptyAssetPublicationDetails = { - id: "", - type: "", - mimetype: "", - size: 0, - checksum: undefined, - channel: "", - reference: "", - tags: [], - url: "", - }; - state.assetPublicationDetails = emptyAssetPublicationDetails; - state.errorAssetPublicationDetails = action.error; - console.error(action.error); - }) - // fetchAccessPolicies - .addCase(fetchAccessPolicies.pending, (state) => { - state.statusPolicies = 'loading'; - }) - .addCase(fetchAccessPolicies.fulfilled, (state, action: PayloadAction< - EventDetailsState["policies"] - >) => { - state.statusPolicies = 'succeeded'; - state.policies = action.payload; - }) - .addCase(fetchAccessPolicies.rejected, (state, action) => { - state.statusPolicies = 'failed'; - state.errorPolicies = action.error; - console.error(action.error); - }) - // fetchComments - .addCase(fetchComments.pending, (state) => { - state.statusComments = 'loading'; - }) - .addCase(fetchComments.fulfilled, (state, action: PayloadAction<{ - comments: EventDetailsState["comments"], - commentReasons: EventDetailsState["commentReasons"], - }>) => { - state.statusComments = 'succeeded'; - const eventDetails = action.payload; - state.comments = eventDetails.comments; - state.commentReasons = eventDetails.commentReasons; - }) - .addCase(fetchComments.rejected, (state, action) => { - state.statusComments = 'failed'; - state.errorComments = action.error; - console.error(action.error); - }) - // fetchEventPublications - .addCase(fetchEventPublications.pending, (state) => { - state.statusPublications = 'loading'; - }) - .addCase(fetchEventPublications.fulfilled, (state, action: PayloadAction< - EventDetailsState["publications"] - >) => { - state.statusPublications = 'succeeded'; - state.publications = action.payload; - }) - .addCase(fetchEventPublications.rejected, (state, action) => { - state.statusPublications = 'failed'; - state.errorPublications = action.error; - console.error(action.error); - }) - // saveComment - .addCase(saveComment.pending, (state) => { - state.statusSaveComment = 'loading'; - }) - .addCase(saveComment.fulfilled, (state) => { - state.statusSaveComment = 'succeeded'; - }) - .addCase(saveComment.rejected, (state, action) => { - state.statusSaveComment = 'failed'; - state.errorSaveComment = action.error; - console.error(action.error); - }) - // saveCommentReply - .addCase(saveCommentReply.pending, (state) => { - state.statusSaveCommentReply = 'loading'; - }) - .addCase(saveCommentReply.fulfilled, (state) => { - state.statusSaveCommentReply = 'succeeded'; - }) - .addCase(saveCommentReply.rejected, (state, action) => { - state.statusSaveCommentReply = 'failed'; - state.errorSaveCommentReply = action.error; - console.error(action.error); - }) - // updateComment - .addCase(updateComment.pending, (state) => { - state.statusUpdateComment = 'loading'; - }) - .addCase(updateComment.fulfilled, (state) => { - state.statusUpdateComment = 'succeeded'; - }) - .addCase(updateComment.rejected, (state, action) => { - state.statusUpdateComment = 'failed'; - state.errorUpdateComment = action.error; - console.error(action.error); - }) - // fetchSchedulingInfo - .addCase(fetchSchedulingInfo.pending, (state) => { - state.statusScheduling = 'loading'; - }) - .addCase(fetchSchedulingInfo.fulfilled, (state, action: PayloadAction< - EventDetailsState["schedulingSource"] - >) => { - state.statusScheduling = 'succeeded'; - state.schedulingSource = action.payload; - state.scheduling.hasProperties = true; - }) - .addCase(fetchSchedulingInfo.rejected, (state, action) => { - // This usually means we have a non-scheduled event - state.statusScheduling = 'failed'; - const emptySchedulingSource = { - start: { - date: "", - hour: undefined, - minute: undefined, - }, - duration: { - hour: undefined, - minute: undefined, - }, - end: { - date: "", - hour: undefined, - minute: undefined, - }, - device: { - id: "", - name: "", - inputs: [], - inputMethods: [], - }, - agentId: undefined, - agentConfiguration: {}, - }; - state.schedulingSource = emptySchedulingSource; - state.scheduling.hasProperties = false; - state.errorScheduling = action.error; - console.debug(action.error); - }) - // saveSchedulingInfo - .addCase(saveSchedulingInfo.pending, (state) => { - state.statusSaveScheduling = 'loading'; - }) - .addCase(saveSchedulingInfo.fulfilled, (state, action: PayloadAction< - EventDetailsState["schedulingSource"] - >) => { - state.statusSaveScheduling = 'succeeded'; - state.schedulingSource = action.payload; - }) - .addCase(saveSchedulingInfo.rejected, (state, action) => { - state.statusSaveScheduling = 'failed'; - state.errorSaveScheduling = action.error; - console.error(action.error); - }) - // checkConflicts - .addCase(checkConflicts.pending, (state) => { - state.statusCheckConflicts = 'loading'; - }) - .addCase(checkConflicts.fulfilled, (state, action: PayloadAction<{ - conflicts: EventDetailsState["schedulingConflicts"], - hasSchedulingConflicts: EventDetailsState["hasSchedulingConflicts"], - }>) => { - state.statusCheckConflicts = 'succeeded'; - const eventDetails = action.payload; - state.schedulingConflicts = eventDetails.conflicts; - state.hasSchedulingConflicts = eventDetails.hasSchedulingConflicts; - }) - .addCase(checkConflicts.rejected, (state, action) => { - state.statusCheckConflicts = 'failed'; - state.errorCheckConflicts = action.error; - console.error(action.error); - }) - // fetchWorkflows - .addCase(fetchWorkflows.pending, (state) => { - state.statusWorkflows = 'loading'; - }) - .addCase(fetchWorkflows.fulfilled, (state, action: PayloadAction< - EventDetailsState["workflows"] - >) => { - state.statusWorkflows = 'succeeded'; - state.workflows = action.payload; - if ("workflowId" in state.workflows.workflow && !!state.workflows.workflow.workflowId) { - state.workflowConfiguration = state.workflows.workflow; - } else { - state.workflowConfiguration = state.baseWorkflow; - } - }) - .addCase(fetchWorkflows.rejected, (state, action) => { - state.statusWorkflows = 'failed'; - state.errorWorkflows = action.error; - console.error(action.error); - }) - // fetchWorkflowDetails - .addCase(fetchWorkflowDetails.pending, (state) => { - state.statusWorkflowDetails = 'loading'; - }) - .addCase(fetchWorkflowDetails.fulfilled, (state, action: PayloadAction< - EventDetailsState["workflows"]["workflow"] - >) => { - state.statusWorkflowDetails = 'succeeded'; - state.workflows.workflow = action.payload; - }) - .addCase(fetchWorkflowDetails.rejected, (state, action) => { - state.statusWorkflowDetails = 'failed'; - // This is the empty workflow data from the original reducer - // TODO: Figure out why it is so vastly different from our initial state - // and maybe fix our initial state if this is actually correct - const emptyWorkflowData = { - workflowId: "", - description: "", - }; - state.workflows.workflow = emptyWorkflowData; - state.errorWorkflowDetails = action.error; - }) - // performWorkflowAction - .addCase(performWorkflowAction.pending, (state) => { - state.statusDoWorkflowAction = 'loading'; - }) - .addCase(performWorkflowAction.fulfilled, (state) => { - state.statusDoWorkflowAction = 'succeeded'; - }) - .addCase(performWorkflowAction.rejected, (state, action) => { - state.statusDoWorkflowAction = 'failed'; - state.errorDoWorkflowAction = action.error; - console.error(action.error); - }) - // deleteWorkflow - .addCase(deleteWorkflow.pending, (state) => { - state.statusDeleteWorkflow = 'loading'; - }) - .addCase(deleteWorkflow.fulfilled, (state, action: PayloadAction< - EventDetailsState["workflows"]["entries"] - >) => { - state.statusDeleteWorkflow = 'succeeded'; - state.workflows.entries = action.payload; - }) - .addCase(deleteWorkflow.rejected, (state, action) => { - state.statusDeleteWorkflow = 'failed'; - state.errorDeleteWorkflow = action.error; - console.error(action.error); - }) - // fetchWorkflowOperations - .addCase(fetchWorkflowOperations.pending, (state) => { - state.statusWorkflowOperations = 'loading'; - }) - .addCase(fetchWorkflowOperations.fulfilled, (state, action: PayloadAction< - EventDetailsState["workflowOperations"] - >) => { - state.statusWorkflowOperations = 'succeeded'; - state.workflowOperations = action.payload; - }) - .addCase(fetchWorkflowOperations.rejected, (state, action) => { - state.statusWorkflowOperations = 'failed'; - state.workflowOperations = { entries: [] }; - state.errorWorkflowOperations = action.error; - }) - // fetchWorkflowOperationDetails - .addCase(fetchWorkflowOperationDetails.pending, (state) => { - state.statusWorkflowOperationDetails = 'loading'; - }) - .addCase(fetchWorkflowOperationDetails.fulfilled, (state, action: PayloadAction< - EventDetailsState["workflowOperationDetails"] - >) => { - state.statusWorkflowOperationDetails = 'succeeded'; - state.workflowOperationDetails = action.payload; - }) - .addCase(fetchWorkflowOperationDetails.rejected, (state, action) => { - state.statusWorkflowOperationDetails = 'failed'; - const emptyOperationDetails = { - completed: "", - description: "", - exception_handler_workflow: "", - execution_host: "", - fail_on_error: false, - failed_attempts: 0, - job: 0, - max_attempts: 0, - name: "", - retry_strategy: "", - started: "", - state: "", - time_in_queue: 0, - }; - state.workflowOperationDetails = emptyOperationDetails; - state.errorWorkflowOperationDetails= action.error; - }) - // fetchWorkflowErrors - .addCase(fetchWorkflowErrors.pending, (state) => { - state.statusWorkflowErrors = 'loading'; - }) - .addCase(fetchWorkflowErrors.fulfilled, (state, action: PayloadAction< - EventDetailsState["workflowErrors"] - >) => { - state.statusWorkflowErrors = 'succeeded'; - state.workflowErrors = action.payload; - }) - .addCase(fetchWorkflowErrors.rejected, (state, action) => { - state.statusWorkflowErrors = 'failed'; - state.workflowErrors = { entries: [] }; - state.errorWorkflowOperations = action.error; - }) - // fetchWorkflowErrorDetails - .addCase(fetchWorkflowErrorDetails.pending, (state) => { - state.statusWorkflowErrorDetails = 'loading'; - }) - .addCase(fetchWorkflowErrorDetails.fulfilled, (state, action: PayloadAction< - EventDetailsState["workflowErrorDetails"] - >) => { - state.statusWorkflowErrorDetails = 'succeeded'; - state.workflowErrorDetails = action.payload; - }) - .addCase(fetchWorkflowErrorDetails.rejected, (state, action) => { - state.statusWorkflowErrorDetails = 'failed'; - state.workflowErrorDetails = { - description: "", - details: [], - id: 0, - job_id: 0, - processing_host: "", - service_type: "", - severity: "", - technical_details: "", - timestamp: "", - title: "", - }; - state.errorWorkflowOperationDetails = action.error; - }) - // fetchEventStatistics - .addCase(fetchEventStatistics.pending, (state) => { - state.statusStatistics = 'loading'; - }) - .addCase(fetchEventStatistics.fulfilled, (state, action: PayloadAction<{ - statistics: EventDetailsState["statistics"], - hasError: EventDetailsState["hasStatisticsError"], - }>) => { - state.statusStatistics = 'succeeded'; - const eventDetails = action.payload; - state.statistics = eventDetails.statistics; - state.hasStatisticsError = eventDetails.hasError; - }) - .addCase(fetchEventStatistics.rejected, (state, action) => { - state.statusStatistics = 'failed'; - state.statistics = []; - state.hasStatisticsError = true; - state.errorStatistics = action.error; - console.error(action.error); - }) - //fetchEventStatisticsValueUpdate - .addCase(fetchEventStatisticsValueUpdate.pending, (state) => { - state.statusStatisticsValue = 'loading'; - }) - .addCase(fetchEventStatisticsValueUpdate.fulfilled, (state, action: PayloadAction< - any - >) => { - state.statusStatisticsValue = 'succeeded'; - state.statistics = action.payload; - }) - .addCase(fetchEventStatisticsValueUpdate.rejected, (state, action) => { - state.statusStatisticsValue = 'failed'; - state.statistics = []; - state.errorStatisticsValue = action.error; - console.error(action.error); - }) - .addCase(updateMetadata.rejected, (state, action) => { - console.error(action.error); - }) - .addCase(updateExtendedMetadata.rejected, (state, action) => { - console.error(action.error); - }) - .addCase(fetchHasActiveTransactions.rejected, (state, action) => { - console.error(action.error); - }) - .addCase(deleteComment.rejected, (state, action) => { - console.error(action.error); - }) - // fetch Tobira data - .addCase(fetchEventDetailsTobira.pending, (state) => { - state.statusTobiraData = 'loading'; - }) - .addCase(fetchEventDetailsTobira.fulfilled, (state, action: PayloadAction< - EventDetailsState['tobiraData'] - >) => { - state.statusTobiraData = 'succeeded'; - state.tobiraData = action.payload; - state.errorTobiraData = null; - }) - .addCase(fetchEventDetailsTobira.rejected, (state, action) => { - state.statusTobiraData = 'failed'; - state.errorTobiraData = action.error; - }) - } + name: 'eventDetails', + initialState, + reducers: { + setShowModal(state, action: PayloadAction< + EventDetailsState["modal"]["show"] + >) { + state.modal.show = action.payload; + }, + setModalPage(state, action: PayloadAction< + EventDetailsState["modal"]["page"] + >) { + state.modal.page = action.payload; + }, + setModalEvent(state, action: PayloadAction< + EventDetailsState["modal"]["event"] + >) { + state.modal.event = action.payload; + }, + setModalWorkflowId(state, action: PayloadAction< + EventDetailsState["modal"]["workflowId"] + >) { + state.modal.workflowId = action.payload; + }, + setModalWorkflowTabHierarchy(state, action: PayloadAction< + EventDetailsState["modal"]["workflowTabHierarchy"] + >) { + state.modal.workflowTabHierarchy = action.payload; + }, + setModalAssetsTabHierarchy(state, action: PayloadAction< + EventDetailsState["modal"]["assetsTabHierarchy"] + >) { + state.modal.assetsTabHierarchy = action.payload; + }, + setEventMetadata(state, action: PayloadAction< + EventDetailsState["metadata"] + >) { + state.metadata = action.payload; + }, + setExtendedEventMetadata(state, action: PayloadAction< + EventDetailsState["extendedMetadata"] + >) { + state.extendedMetadata = action.payload; + }, + setEventWorkflow(state, action: PayloadAction< + EventDetailsState["workflows"]["workflow"] + >) { + state.workflows.workflow = action.payload; + }, + setEventWorkflowDefinitions(state, action: PayloadAction<{ + workflows: EventDetailsState["workflows"], + workflowDefinitions: EventDetailsState["workflowDefinitions"], + }>) { + if ("workflowId" in action.payload.workflows.workflow) { + state.baseWorkflow = { ...action.payload.workflows.workflow }; + } + state.workflows = action.payload.workflows; + state.workflowDefinitions = action.payload.workflowDefinitions; + }, + setEventWorkflowConfiguration(state, action: PayloadAction<{ + workflowConfiguration: EventDetailsState["workflowConfiguration"], + }>) { + state.workflowConfiguration = action.payload.workflowConfiguration; + }, + }, + // These are used for thunks + extraReducers: builder => { + builder + // fetchMetadata + .addCase(fetchMetadata.pending, (state) => { + state.statusMetadata = 'loading'; + }) + .addCase(fetchMetadata.fulfilled, (state, action: PayloadAction<{ + metadata: EventDetailsState["metadata"], + extendedMetadata: EventDetailsState["extendedMetadata"], + }>) => { + state.statusMetadata = 'succeeded'; + const eventDetails = action.payload; + state.metadata = eventDetails.metadata; + state.extendedMetadata = eventDetails.extendedMetadata; + }) + .addCase(fetchMetadata.rejected, (state, action) => { + state.statusMetadata = 'failed'; + state.metadata = { + title: "", + flavor: "", + fields: [], + }; + state.extendedMetadata = []; + state.errorMetadata = action.error; + console.error(action.error); + }) + // fetchAssets + .addCase(fetchAssets.pending, (state) => { + state.statusAssets = 'loading'; + }) + .addCase(fetchAssets.fulfilled, (state, action: PayloadAction<{ + assets: EventDetailsState["assets"], + transactionsReadOnly: EventDetailsState["transactionsReadOnly"], + uploadAssetOptions: EventDetailsState["uploadAssetOptions"], + }>) => { + state.statusAssets = 'succeeded'; + const eventDetails = action.payload; + state.assets = eventDetails.assets; + state.transactionsReadOnly = eventDetails.transactionsReadOnly; + state.uploadAssetOptions = eventDetails.uploadAssetOptions; + }) + .addCase(fetchAssets.rejected, (state, action) => { + state.statusAssets = 'failed'; + const emptyAssets = { + attachments: 0, + catalogs: 0, + media: 0, + publications: 0, + }; + state.assets = emptyAssets; + state.transactionsReadOnly = false; + state.uploadAssetOptions = []; + state.errorAssets = action.error; + console.error(action.error); + }) + // fetchAssetAttachments + .addCase(fetchAssetAttachments.pending, (state) => { + state.statusAssetAttachments = 'loading'; + }) + .addCase(fetchAssetAttachments.fulfilled, (state, action: PayloadAction< + EventDetailsState["assetAttachments"] + >) => { + state.statusAssetAttachments = 'succeeded'; + state.assetAttachments = action.payload; + }) + .addCase(fetchAssetAttachments.rejected, (state, action) => { + state.statusAssetAttachments = 'failed'; + state.assetAttachments = []; + state.errorAssetAttachments = action.error; + console.error(action.error); + }) + // fetchAssetAttachmentDetails + .addCase(fetchAssetAttachmentDetails.pending, (state) => { + state.statusAssetAttachments = 'loading'; + }) + .addCase(fetchAssetAttachmentDetails.fulfilled, (state, action: PayloadAction< + EventDetailsState["assetAttachmentDetails"] + >) => { + state.statusAssetAttachments = 'succeeded'; + state.assetAttachmentDetails = action.payload; + }) + .addCase(fetchAssetAttachmentDetails.rejected, (state, action) => { + state.statusAssetAttachments = 'failed'; + const emptyAssetAttachmentDetails = { + id: "", + type: "", + mimetype: "", + size: 0, + checksum: undefined, + reference: "", + tags: [], + url: "", + }; + state.assetAttachmentDetails = emptyAssetAttachmentDetails; + state.errorAssetAttachments = action.error; + console.error(action.error); + }) + // fetchAssetCatalogs + .addCase(fetchAssetCatalogs.pending, (state) => { + state.statusAssetCatalogs = 'loading'; + }) + .addCase(fetchAssetCatalogs.fulfilled, (state, action: PayloadAction< + EventDetailsState["assetCatalogs"] + >) => { + state.statusAssetCatalogs = 'succeeded'; + state.assetCatalogs = action.payload; + }) + .addCase(fetchAssetCatalogs.rejected, (state, action) => { + state.statusAssetCatalogs = 'failed'; + state.assetCatalogs = []; + state.errorAssetCatalogs = action.error; + console.error(action.error); + }) + // fetchAssetCatalogDetails + .addCase(fetchAssetCatalogDetails.pending, (state) => { + state.statusAssetCatalogDetails = 'loading'; + }) + .addCase(fetchAssetCatalogDetails.fulfilled, (state, action: PayloadAction< + EventDetailsState["assetCatalogDetails"] + >) => { + state.statusAssetCatalogDetails = 'succeeded'; + state.assetCatalogDetails = action.payload; + }) + .addCase(fetchAssetCatalogDetails.rejected, (state, action) => { + state.statusAssetCatalogDetails = 'failed'; + const emptyAssetCatalogDetails = { + id: "", + type: "", + mimetype: "", + size: 0, + checksum: undefined, + reference: "", + tags: [], + url: "", + }; + state.assetCatalogDetails = emptyAssetCatalogDetails; + state.errorAssetCatalogDetails = action.error; + console.error(action.error); + }) + // fetchAssetMedia + .addCase(fetchAssetMedia.pending, (state) => { + state.statusAssetMedia = 'loading'; + }) + .addCase(fetchAssetMedia.fulfilled, (state, action: PayloadAction< + EventDetailsState["assetMedia"] + >) => { + state.statusAssetMedia = 'succeeded'; + state.assetMedia = action.payload; + }) + .addCase(fetchAssetMedia.rejected, (state, action) => { + state.statusAssetMedia = 'failed'; + state.assetMedia = []; + state.errorAssetMedia = action.error; + console.error(action.error); + }) + // fetchAssetMediaDetails + .addCase(fetchAssetMediaDetails.pending, (state) => { + state.statusAssetMediaDetails = 'loading'; + }) + .addCase(fetchAssetMediaDetails.fulfilled, (state, action: PayloadAction< + EventDetailsState["assetMediaDetails"] + >) => { + state.statusAssetMediaDetails = 'succeeded'; + state.assetMediaDetails = action.payload; + }) + .addCase(fetchAssetMediaDetails.rejected, (state, action) => { + state.statusAssetMediaDetails = 'failed'; + const emptyAssetMediaDetails = { + id: "", + type: "", + mimetype: "", + tags: [], + duration: 0, + size: 0, + checksum: undefined, + reference: "", + has_audio: false, + has_subtitle: false, + has_video: false, + url: "", + streams: { + audio: [], + video: [], + }, + video: undefined, + }; + state.assetMediaDetails = emptyAssetMediaDetails; + state.errorAssetMediaDetails = action.error; + console.error(action.error); + }) + // fetchAssetPublications + .addCase(fetchAssetPublications.pending, (state) => { + state.statusAssetPublications = 'loading'; + }) + .addCase(fetchAssetPublications.fulfilled, (state, action: PayloadAction< + EventDetailsState["assetPublications"] + >) => { + state.statusAssetPublications = 'succeeded'; + state.assetPublications = action.payload; + }) + .addCase(fetchAssetPublications.rejected, (state, action) => { + state.statusAssetPublications = 'failed'; + state.assetPublications = []; + state.errorAssetPublications = action.error; + console.error(action.error); + }) + // fetchAssetPublicationDetails + .addCase(fetchAssetPublicationDetails.pending, (state) => { + state.statusAssetPublicationDetails = 'loading'; + }) + .addCase(fetchAssetPublicationDetails.fulfilled, (state, action: PayloadAction< + EventDetailsState["assetPublicationDetails"] + >) => { + state.statusAssetPublicationDetails = 'succeeded'; + state.assetPublicationDetails = action.payload; + }) + .addCase(fetchAssetPublicationDetails.rejected, (state, action) => { + state.statusAssetPublicationDetails = 'failed'; + const emptyAssetPublicationDetails = { + id: "", + type: "", + mimetype: "", + size: 0, + checksum: undefined, + channel: "", + reference: "", + tags: [], + url: "", + }; + state.assetPublicationDetails = emptyAssetPublicationDetails; + state.errorAssetPublicationDetails = action.error; + console.error(action.error); + }) + // fetchAccessPolicies + .addCase(fetchAccessPolicies.pending, (state) => { + state.statusPolicies = 'loading'; + }) + .addCase(fetchAccessPolicies.fulfilled, (state, action: PayloadAction< + EventDetailsState["policies"] + >) => { + state.statusPolicies = 'succeeded'; + state.policies = action.payload; + }) + .addCase(fetchAccessPolicies.rejected, (state, action) => { + state.statusPolicies = 'failed'; + state.errorPolicies = action.error; + console.error(action.error); + }) + // fetchComments + .addCase(fetchComments.pending, (state) => { + state.statusComments = 'loading'; + }) + .addCase(fetchComments.fulfilled, (state, action: PayloadAction<{ + comments: EventDetailsState["comments"], + commentReasons: EventDetailsState["commentReasons"], + }>) => { + state.statusComments = 'succeeded'; + const eventDetails = action.payload; + state.comments = eventDetails.comments; + state.commentReasons = eventDetails.commentReasons; + }) + .addCase(fetchComments.rejected, (state, action) => { + state.statusComments = 'failed'; + state.errorComments = action.error; + console.error(action.error); + }) + // fetchEventPublications + .addCase(fetchEventPublications.pending, (state) => { + state.statusPublications = 'loading'; + }) + .addCase(fetchEventPublications.fulfilled, (state, action: PayloadAction< + EventDetailsState["publications"] + >) => { + state.statusPublications = 'succeeded'; + state.publications = action.payload; + }) + .addCase(fetchEventPublications.rejected, (state, action) => { + state.statusPublications = 'failed'; + state.errorPublications = action.error; + console.error(action.error); + }) + // saveComment + .addCase(saveComment.pending, (state) => { + state.statusSaveComment = 'loading'; + }) + .addCase(saveComment.fulfilled, (state) => { + state.statusSaveComment = 'succeeded'; + }) + .addCase(saveComment.rejected, (state, action) => { + state.statusSaveComment = 'failed'; + state.errorSaveComment = action.error; + console.error(action.error); + }) + // saveCommentReply + .addCase(saveCommentReply.pending, (state) => { + state.statusSaveCommentReply = 'loading'; + }) + .addCase(saveCommentReply.fulfilled, (state) => { + state.statusSaveCommentReply = 'succeeded'; + }) + .addCase(saveCommentReply.rejected, (state, action) => { + state.statusSaveCommentReply = 'failed'; + state.errorSaveCommentReply = action.error; + console.error(action.error); + }) + // updateComment + .addCase(updateComment.pending, (state) => { + state.statusUpdateComment = 'loading'; + }) + .addCase(updateComment.fulfilled, (state) => { + state.statusUpdateComment = 'succeeded'; + }) + .addCase(updateComment.rejected, (state, action) => { + state.statusUpdateComment = 'failed'; + state.errorUpdateComment = action.error; + console.error(action.error); + }) + // fetchSchedulingInfo + .addCase(fetchSchedulingInfo.pending, (state) => { + state.statusScheduling = 'loading'; + }) + .addCase(fetchSchedulingInfo.fulfilled, (state, action: PayloadAction< + EventDetailsState["schedulingSource"] + >) => { + state.statusScheduling = 'succeeded'; + state.schedulingSource = action.payload; + state.scheduling.hasProperties = true; + }) + .addCase(fetchSchedulingInfo.rejected, (state, action) => { + // This usually means we have a non-scheduled event + state.statusScheduling = 'failed'; + const emptySchedulingSource = { + start: { + date: "", + hour: undefined, + minute: undefined, + }, + duration: { + hour: undefined, + minute: undefined, + }, + end: { + date: "", + hour: undefined, + minute: undefined, + }, + device: { + id: "", + name: "", + inputs: [], + inputMethods: [], + }, + agentId: undefined, + agentConfiguration: {}, + }; + state.schedulingSource = emptySchedulingSource; + state.scheduling.hasProperties = false; + state.errorScheduling = action.error; + console.debug(action.error); + }) + // saveSchedulingInfo + .addCase(saveSchedulingInfo.pending, (state) => { + state.statusSaveScheduling = 'loading'; + }) + .addCase(saveSchedulingInfo.fulfilled, (state, action: PayloadAction< + EventDetailsState["schedulingSource"] + >) => { + state.statusSaveScheduling = 'succeeded'; + state.schedulingSource = action.payload; + }) + .addCase(saveSchedulingInfo.rejected, (state, action) => { + state.statusSaveScheduling = 'failed'; + state.errorSaveScheduling = action.error; + console.error(action.error); + }) + // checkConflicts + .addCase(checkConflicts.pending, (state) => { + state.statusCheckConflicts = 'loading'; + }) + .addCase(checkConflicts.fulfilled, (state, action: PayloadAction<{ + conflicts: EventDetailsState["schedulingConflicts"], + hasSchedulingConflicts: EventDetailsState["hasSchedulingConflicts"], + }>) => { + state.statusCheckConflicts = 'succeeded'; + const eventDetails = action.payload; + state.schedulingConflicts = eventDetails.conflicts; + state.hasSchedulingConflicts = eventDetails.hasSchedulingConflicts; + }) + .addCase(checkConflicts.rejected, (state, action) => { + state.statusCheckConflicts = 'failed'; + state.errorCheckConflicts = action.error; + console.error(action.error); + }) + // fetchWorkflows + .addCase(fetchWorkflows.pending, (state) => { + state.statusWorkflows = 'loading'; + }) + .addCase(fetchWorkflows.fulfilled, (state, action: PayloadAction< + EventDetailsState["workflows"] + >) => { + state.statusWorkflows = 'succeeded'; + state.workflows = action.payload; + if ("workflowId" in state.workflows.workflow && !!state.workflows.workflow.workflowId) { + state.workflowConfiguration = state.workflows.workflow; + } else { + state.workflowConfiguration = state.baseWorkflow; + } + }) + .addCase(fetchWorkflows.rejected, (state, action) => { + state.statusWorkflows = 'failed'; + state.errorWorkflows = action.error; + console.error(action.error); + }) + // fetchWorkflowDetails + .addCase(fetchWorkflowDetails.pending, (state) => { + state.statusWorkflowDetails = 'loading'; + }) + .addCase(fetchWorkflowDetails.fulfilled, (state, action: PayloadAction< + EventDetailsState["workflows"]["workflow"] + >) => { + state.statusWorkflowDetails = 'succeeded'; + state.workflows.workflow = action.payload; + }) + .addCase(fetchWorkflowDetails.rejected, (state, action) => { + state.statusWorkflowDetails = 'failed'; + // This is the empty workflow data from the original reducer + // TODO: Figure out why it is so vastly different from our initial state + // and maybe fix our initial state if this is actually correct + const emptyWorkflowData = { + workflowId: "", + description: "", + }; + state.workflows.workflow = emptyWorkflowData; + state.errorWorkflowDetails = action.error; + }) + // performWorkflowAction + .addCase(performWorkflowAction.pending, (state) => { + state.statusDoWorkflowAction = 'loading'; + }) + .addCase(performWorkflowAction.fulfilled, (state) => { + state.statusDoWorkflowAction = 'succeeded'; + }) + .addCase(performWorkflowAction.rejected, (state, action) => { + state.statusDoWorkflowAction = 'failed'; + state.errorDoWorkflowAction = action.error; + console.error(action.error); + }) + // deleteWorkflow + .addCase(deleteWorkflow.pending, (state) => { + state.statusDeleteWorkflow = 'loading'; + }) + .addCase(deleteWorkflow.fulfilled, (state, action: PayloadAction< + EventDetailsState["workflows"]["entries"] + >) => { + state.statusDeleteWorkflow = 'succeeded'; + state.workflows.entries = action.payload; + }) + .addCase(deleteWorkflow.rejected, (state, action) => { + state.statusDeleteWorkflow = 'failed'; + state.errorDeleteWorkflow = action.error; + console.error(action.error); + }) + // fetchWorkflowOperations + .addCase(fetchWorkflowOperations.pending, (state) => { + state.statusWorkflowOperations = 'loading'; + }) + .addCase(fetchWorkflowOperations.fulfilled, (state, action: PayloadAction< + EventDetailsState["workflowOperations"] + >) => { + state.statusWorkflowOperations = 'succeeded'; + state.workflowOperations = action.payload; + }) + .addCase(fetchWorkflowOperations.rejected, (state, action) => { + state.statusWorkflowOperations = 'failed'; + state.workflowOperations = { entries: [] }; + state.errorWorkflowOperations = action.error; + }) + // fetchWorkflowOperationDetails + .addCase(fetchWorkflowOperationDetails.pending, (state) => { + state.statusWorkflowOperationDetails = 'loading'; + }) + .addCase(fetchWorkflowOperationDetails.fulfilled, (state, action: PayloadAction< + EventDetailsState["workflowOperationDetails"] + >) => { + state.statusWorkflowOperationDetails = 'succeeded'; + state.workflowOperationDetails = action.payload; + }) + .addCase(fetchWorkflowOperationDetails.rejected, (state, action) => { + state.statusWorkflowOperationDetails = 'failed'; + const emptyOperationDetails = { + completed: "", + description: "", + exception_handler_workflow: "", + execution_host: "", + fail_on_error: false, + failed_attempts: 0, + job: 0, + max_attempts: 0, + name: "", + retry_strategy: "", + started: "", + state: "", + time_in_queue: 0, + }; + state.workflowOperationDetails = emptyOperationDetails; + state.errorWorkflowOperationDetails= action.error; + }) + // fetchWorkflowErrors + .addCase(fetchWorkflowErrors.pending, (state) => { + state.statusWorkflowErrors = 'loading'; + }) + .addCase(fetchWorkflowErrors.fulfilled, (state, action: PayloadAction< + EventDetailsState["workflowErrors"] + >) => { + state.statusWorkflowErrors = 'succeeded'; + state.workflowErrors = action.payload; + }) + .addCase(fetchWorkflowErrors.rejected, (state, action) => { + state.statusWorkflowErrors = 'failed'; + state.workflowErrors = { entries: [] }; + state.errorWorkflowOperations = action.error; + }) + // fetchWorkflowErrorDetails + .addCase(fetchWorkflowErrorDetails.pending, (state) => { + state.statusWorkflowErrorDetails = 'loading'; + }) + .addCase(fetchWorkflowErrorDetails.fulfilled, (state, action: PayloadAction< + EventDetailsState["workflowErrorDetails"] + >) => { + state.statusWorkflowErrorDetails = 'succeeded'; + state.workflowErrorDetails = action.payload; + }) + .addCase(fetchWorkflowErrorDetails.rejected, (state, action) => { + state.statusWorkflowErrorDetails = 'failed'; + state.workflowErrorDetails = { + description: "", + details: [], + id: 0, + job_id: 0, + processing_host: "", + service_type: "", + severity: "", + technical_details: "", + timestamp: "", + title: "", + }; + state.errorWorkflowOperationDetails = action.error; + }) + // fetchEventStatistics + .addCase(fetchEventStatistics.pending, (state) => { + state.statusStatistics = 'loading'; + }) + .addCase(fetchEventStatistics.fulfilled, (state, action: PayloadAction<{ + statistics: EventDetailsState["statistics"], + hasError: EventDetailsState["hasStatisticsError"], + }>) => { + state.statusStatistics = 'succeeded'; + const eventDetails = action.payload; + state.statistics = eventDetails.statistics; + state.hasStatisticsError = eventDetails.hasError; + }) + .addCase(fetchEventStatistics.rejected, (state, action) => { + state.statusStatistics = 'failed'; + state.statistics = []; + state.hasStatisticsError = true; + state.errorStatistics = action.error; + console.error(action.error); + }) + //fetchEventStatisticsValueUpdate + .addCase(fetchEventStatisticsValueUpdate.pending, (state) => { + state.statusStatisticsValue = 'loading'; + }) + .addCase(fetchEventStatisticsValueUpdate.fulfilled, (state, action: PayloadAction< + any + >) => { + state.statusStatisticsValue = 'succeeded'; + state.statistics = action.payload; + }) + .addCase(fetchEventStatisticsValueUpdate.rejected, (state, action) => { + state.statusStatisticsValue = 'failed'; + state.statistics = []; + state.errorStatisticsValue = action.error; + console.error(action.error); + }) + .addCase(updateMetadata.rejected, (state, action) => { + console.error(action.error); + }) + .addCase(updateExtendedMetadata.rejected, (state, action) => { + console.error(action.error); + }) + .addCase(fetchHasActiveTransactions.rejected, (state, action) => { + console.error(action.error); + }) + .addCase(deleteComment.rejected, (state, action) => { + console.error(action.error); + }) + // fetch Tobira data + .addCase(fetchEventDetailsTobira.pending, (state) => { + state.statusTobiraData = 'loading'; + }) + .addCase(fetchEventDetailsTobira.fulfilled, (state, action: PayloadAction< + EventDetailsState['tobiraData'] + >) => { + state.statusTobiraData = 'succeeded'; + state.tobiraData = action.payload; + state.errorTobiraData = null; + }) + .addCase(fetchEventDetailsTobira.rejected, (state, action) => { + state.statusTobiraData = 'failed'; + state.errorTobiraData = action.error; + }) + } }); export const { - setShowModal, - setModalPage, - setModalEvent, - setModalWorkflowId, - setModalWorkflowTabHierarchy, - setModalAssetsTabHierarchy, - setEventMetadata, - setExtendedEventMetadata, - setEventWorkflow, - setEventWorkflowDefinitions, - setEventWorkflowConfiguration, + setShowModal, + setModalPage, + setModalEvent, + setModalWorkflowId, + setModalWorkflowTabHierarchy, + setModalAssetsTabHierarchy, + setEventMetadata, + setExtendedEventMetadata, + setEventWorkflow, + setEventWorkflowDefinitions, + setEventWorkflowConfiguration, } = eventDetailsSlice.actions; // Export the slice reducer as the default export diff --git a/src/slices/eventSlice.ts b/src/slices/eventSlice.ts index df8174a35f..40193dabe8 100644 --- a/src/slices/eventSlice.ts +++ b/src/slices/eventSlice.ts @@ -3,22 +3,22 @@ import { eventsTableConfig } from "../configs/tableConfigs/eventsTableConfig"; import axios, { AxiosProgressEvent } from 'axios'; import moment from "moment-timezone"; import { - getURLParams, - prepareAccessPolicyRulesForPost, - prepareMetadataFieldsForPost, - transformMetadataCollection, - transformMetadataFields, + getURLParams, + prepareAccessPolicyRulesForPost, + prepareMetadataFieldsForPost, + transformMetadataCollection, + transformMetadataFields, } from "../utils/resourceUtils"; import { makeTwoDigits } from "../utils/utils"; import { sourceMetadata } from "../configs/sourceConfig"; import { - NOTIFICATION_CONTEXT, - weekdays, - WORKFLOW_UPLOAD_ASSETS_NON_TRACK, + NOTIFICATION_CONTEXT, + weekdays, + WORKFLOW_UPLOAD_ASSETS_NON_TRACK, } from "../configs/modalConfig"; import { - removeNotification, - addNotification, + removeNotification, + addNotification, } from "./notificationSlice"; import { getAssetUploadOptions, getSchedulingEditedEvents } from '../selectors/eventSelectors'; import { fetchSeriesOptions } from "./seriesSlice"; @@ -34,1152 +34,1152 @@ import { FormikErrors } from 'formik'; * This file contains redux reducer for actions affecting the state of events */ type Comment = { - reason: string, - resolvedStatus: boolean, - modificationDate: string, - replies: { - id: number, - text: string, - creationDate: string, - modificationDate: string, - author: { - name: string, - email?: string, - username: string, - } - }[], - author: { - name: string, - email?: string, - username: string, - }, - id: number, - text: string, - creationDate: string, + reason: string, + resolvedStatus: boolean, + modificationDate: string, + replies: { + id: number, + text: string, + creationDate: string, + modificationDate: string, + author: { + name: string, + email?: string, + username: string, + } + }[], + author: { + name: string, + email?: string, + username: string, + }, + id: number, + text: string, + creationDate: string, } // Strings will be empty if there is no value export type Event = { - agent_id: string, - comments?: Comment[], - date: string, - displayable_status: string, - end_date: string, - event_status: string, - has_comments: boolean, - has_open_comments: boolean, - has_preview: boolean, - id: string, - location: string, - managedAcl: string, - needs_cutting: boolean, - presenters: string[], - publications: Publication[], - selected?: boolean, - series?: { id: string, title: string } - source: string, - start_date: string, - technical_end: string, - technical_presenters: string[], - technical_start: string, - title: string, - workflow_state: string, + agent_id: string, + comments?: Comment[], + date: string, + displayable_status: string, + end_date: string, + event_status: string, + has_comments: boolean, + has_open_comments: boolean, + has_preview: boolean, + id: string, + location: string, + managedAcl: string, + needs_cutting: boolean, + presenters: string[], + publications: Publication[], + selected?: boolean, + series?: { id: string, title: string } + source: string, + start_date: string, + technical_end: string, + technical_presenters: string[], + technical_start: string, + title: string, + workflow_state: string, } export type MetadataField = { - delimiter?: string, - differentValues?: boolean, - collection?: { [key: string]: unknown }[], // different for e.g. languages and presenters - id: string, - label: string, - readOnly: boolean, - required: boolean, - translatable?: boolean, - type: string, - value: string | string[], + delimiter?: string, + differentValues?: boolean, + collection?: { [key: string]: unknown }[], // different for e.g. languages and presenters + id: string, + label: string, + readOnly: boolean, + required: boolean, + translatable?: boolean, + type: string, + value: string | string[], } export type MetadataFieldSelected = MetadataField & { selected: boolean } export type MetadataCatalog = { - title: string, - flavor: string, - fields: MetadataField[], + title: string, + flavor: string, + fields: MetadataField[], } export type EditedEvents = { - changedDeviceInputs: string[], - changedEndTimeHour: string, - changedEndTimeMinutes: string, - changedLocation: string, - changedSeries: string, - changedStartTimeHour: string, - changedStartTimeMinutes: string, - changedTitle: string, - changedWeekday: string, - deviceInputs: string, - endTimeHour: string, - endTimeMinutes: string, - eventId: string, - location: string, - series: string, - startTimeHour: string, - startTimeMinutes: string, - title: string, - weekday: string, + changedDeviceInputs: string[], + changedEndTimeHour: string, + changedEndTimeMinutes: string, + changedLocation: string, + changedSeries: string, + changedStartTimeHour: string, + changedStartTimeMinutes: string, + changedTitle: string, + changedWeekday: string, + deviceInputs: string, + endTimeHour: string, + endTimeMinutes: string, + eventId: string, + location: string, + series: string, + startTimeHour: string, + startTimeMinutes: string, + title: string, + weekday: string, } export type UploadAssetOption = { - accept: string, - displayFallback?: string, - "displayFallback.DETAIL"?: string, - "displayFallback.SHORT"?: string, - displayOrder: number, - flavorSubType: string, - flavorType: string, - id: string, - multiple: boolean, - showAs: string, - title: string, - type: string, - displayOverride?: string, - "displayOverride.SHORT"?: string, - "displayOverride.DETAIL"?: string, + accept: string, + displayFallback?: string, + "displayFallback.DETAIL"?: string, + "displayFallback.SHORT"?: string, + displayOrder: number, + flavorSubType: string, + flavorType: string, + id: string, + multiple: boolean, + showAs: string, + title: string, + type: string, + displayOverride?: string, + "displayOverride.SHORT"?: string, + "displayOverride.DETAIL"?: string, } export type UploadAssetsTrack = UploadAssetOption & { - file?: FileList + file?: FileList } export type Conflict = { - conflicts: { - end: string, - start: string, - title: string, - }[], - eventId: string, + conflicts: { + end: string, + start: string, + title: string, + }[], + eventId: string, } type EventState = { - status: 'uninitialized' | 'loading' | 'succeeded' | 'failed', - error: SerializedError | null, - statusMetadata: 'uninitialized' | 'loading' | 'succeeded' | 'failed', - errorMetadata: SerializedError | null, - statusSchedulingInfo: 'uninitialized' | 'loading' | 'succeeded' | 'failed', - errorSchedulingInfo: SerializedError | null, - statusAssetUploadOptions: 'uninitialized' | 'loading' | 'succeeded' | 'failed', - errorAssetUploadOptions: SerializedError | null, - results: Event[], - columns: TableConfig["columns"], - total: number, - count: number, - offset: number, - limit: number, - showActions: boolean, - metadata: MetadataCatalog, - extendedMetadata: MetadataCatalog[], - isFetchingAssetUploadOptions: boolean, - uploadAssetOptions: UploadAssetOption[], - uploadAssetWorkflow: string | undefined, - schedulingInfo: { - editedEvents: EditedEvents[], - seriesOptions: { - name: string, - value: string, - }[], - }, + status: 'uninitialized' | 'loading' | 'succeeded' | 'failed', + error: SerializedError | null, + statusMetadata: 'uninitialized' | 'loading' | 'succeeded' | 'failed', + errorMetadata: SerializedError | null, + statusSchedulingInfo: 'uninitialized' | 'loading' | 'succeeded' | 'failed', + errorSchedulingInfo: SerializedError | null, + statusAssetUploadOptions: 'uninitialized' | 'loading' | 'succeeded' | 'failed', + errorAssetUploadOptions: SerializedError | null, + results: Event[], + columns: TableConfig["columns"], + total: number, + count: number, + offset: number, + limit: number, + showActions: boolean, + metadata: MetadataCatalog, + extendedMetadata: MetadataCatalog[], + isFetchingAssetUploadOptions: boolean, + uploadAssetOptions: UploadAssetOption[], + uploadAssetWorkflow: string | undefined, + schedulingInfo: { + editedEvents: EditedEvents[], + seriesOptions: { + name: string, + value: string, + }[], + }, } // Fill columns initially with columns defined in eventsTableConfig const initialColumns = eventsTableConfig.columns.map((column) => ({ - deactivated: false, - ...column, + deactivated: false, + ...column, })); // Initial state of events in redux store const initialState: EventState = { - status: 'uninitialized', - error: null, - statusMetadata: 'uninitialized', - errorMetadata: null, - statusSchedulingInfo: 'uninitialized', - errorSchedulingInfo: null, - statusAssetUploadOptions: 'uninitialized', - errorAssetUploadOptions: null, + status: 'uninitialized', + error: null, + statusMetadata: 'uninitialized', + errorMetadata: null, + statusSchedulingInfo: 'uninitialized', + errorSchedulingInfo: null, + statusAssetUploadOptions: 'uninitialized', + errorAssetUploadOptions: null, results: [], - columns: initialColumns, - total: 0, - count: 0, - limit: 0, - offset: 0, - showActions: false, - metadata: { - title: "", - flavor: "", - fields: [], - }, - extendedMetadata: [], - isFetchingAssetUploadOptions: false, - uploadAssetOptions: [], - uploadAssetWorkflow: "", - schedulingInfo: { - editedEvents: [], - seriesOptions: [], - }, + columns: initialColumns, + total: 0, + count: 0, + limit: 0, + offset: 0, + showActions: false, + metadata: { + title: "", + flavor: "", + fields: [], + }, + extendedMetadata: [], + isFetchingAssetUploadOptions: false, + uploadAssetOptions: [], + uploadAssetWorkflow: "", + schedulingInfo: { + editedEvents: [], + seriesOptions: [], + }, }; // fetch events from server export const fetchEvents = createAppAsyncThunk('events/fetchEvents', async (_, { dispatch, getState }) => { - const state = getState(); - let params: ReturnType & { getComments?: boolean } = getURLParams(state, "events"); - - // Only if the notes column is enabled, fetch comment information for events - if (state.table.columns.find(column => column.label === "EVENTS.EVENTS.TABLE.ADMINUI_NOTES" && !column.deactivated)) { - params = { - ...params, - getComments: true - } - } - - // Just make the async request here, and return the response. - // This will automatically dispatch a `pending` action first, - // and then `fulfilled` or `rejected` actions based on the promise. - const res = await axios.get("/admin-ng/event/events.json", { params: params }); - const response = res.data; - - for (let i = 0; response.results.length > i; i++) { - // insert date property - response.results[i] = { - ...response.results[i], - date: response.results[i].start_date, - }; - // insert enabled and hide property of publications, if result has publications - let result = response.results[i]; - if (!!result.publications && result.publications.length > 0) { - let transformedPublications: Publication[] = []; - try { - const resultAction = await dispatch(enrichPublications({ publications: result.publications })); - transformedPublications = unwrapResult(resultAction); - } catch (rejectedValueOrSerializedError) { - console.error(rejectedValueOrSerializedError) - } - response.results[i] = { - ...response.results[i], - publications: transformedPublications, - }; - } - } - const events = response; - - return events; + const state = getState(); + let params: ReturnType & { getComments?: boolean } = getURLParams(state, "events"); + + // Only if the notes column is enabled, fetch comment information for events + if (state.table.columns.find(column => column.label === "EVENTS.EVENTS.TABLE.ADMINUI_NOTES" && !column.deactivated)) { + params = { + ...params, + getComments: true + } + } + + // Just make the async request here, and return the response. + // This will automatically dispatch a `pending` action first, + // and then `fulfilled` or `rejected` actions based on the promise. + const res = await axios.get("/admin-ng/event/events.json", { params: params }); + const response = res.data; + + for (let i = 0; response.results.length > i; i++) { + // insert date property + response.results[i] = { + ...response.results[i], + date: response.results[i].start_date, + }; + // insert enabled and hide property of publications, if result has publications + let result = response.results[i]; + if (!!result.publications && result.publications.length > 0) { + let transformedPublications: Publication[] = []; + try { + const resultAction = await dispatch(enrichPublications({ publications: result.publications })); + transformedPublications = unwrapResult(resultAction); + } catch (rejectedValueOrSerializedError) { + console.error(rejectedValueOrSerializedError) + } + response.results[i] = { + ...response.results[i], + publications: transformedPublications, + }; + } + } + const events = response; + + return events; }); // fetch event metadata from server export const fetchEventMetadata = createAppAsyncThunk('events/fetchEventMetadata', async (_, { rejectWithValue }) => { - let data = await axios.get("/admin-ng/event/new/metadata"); - const response = await data.data; - - const mainCatalog = "dublincore/episode"; - let metadata: EventState["metadata"] | undefined = undefined; - const extendedMetadata = []; - - for (const metadataCatalog of response) { - if (metadataCatalog.flavor === mainCatalog) { - metadata = transformMetadataCollection({ ...metadataCatalog }); - } else { - extendedMetadata.push( - transformMetadataCollection({ ...metadataCatalog }) - ); - } - } - - if (!metadata) { - console.error("Main metadata catalog is missing"); - return rejectWithValue("Main metadata catalog is missing") - } - - return { metadata, extendedMetadata } + let data = await axios.get("/admin-ng/event/new/metadata"); + const response = await data.data; + + const mainCatalog = "dublincore/episode"; + let metadata: EventState["metadata"] | undefined = undefined; + const extendedMetadata = []; + + for (const metadataCatalog of response) { + if (metadataCatalog.flavor === mainCatalog) { + metadata = transformMetadataCollection({ ...metadataCatalog }); + } else { + extendedMetadata.push( + transformMetadataCollection({ ...metadataCatalog }) + ); + } + } + + if (!metadata) { + console.error("Main metadata catalog is missing"); + return rejectWithValue("Main metadata catalog is missing") + } + + return { metadata, extendedMetadata } }); // get merged metadata for provided event ids export const postEditMetadata = createAppAsyncThunk('events/postEditMetadata', async (ids: Event["id"][]) => { - let formData = new URLSearchParams(); - formData.append("eventIds", JSON.stringify(ids)); - - let data = await axios.post( - "/admin-ng/event/events/metadata.json", - formData, - { - headers: { - "Content-Type": "application/x-www-form-urlencoded", - }, - } - ); - let response = await data.data; - - // transform response - let metadata = transformMetadataFields(response.metadata) - .map(field => ({ ...field, selected: false })); - return { - mergedMetadata: metadata, - notFound: response.notFound, - merged: response.merged, - runningWorkflow: response.runningWorkflow, - }; + let formData = new URLSearchParams(); + formData.append("eventIds", JSON.stringify(ids)); + + let data = await axios.post( + "/admin-ng/event/events/metadata.json", + formData, + { + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + } + ); + let response = await data.data; + + // transform response + let metadata = transformMetadataFields(response.metadata) + .map(field => ({ ...field, selected: false })); + return { + mergedMetadata: metadata, + notFound: response.notFound, + merged: response.merged, + runningWorkflow: response.runningWorkflow, + }; }); export const updateBulkMetadata = createAppAsyncThunk('events/updateBulkMetadata', async (params: { - metadataFields: { - merged: string[], - mergedMetadata: MetadataFieldSelected[], - notFound?: string[], - runningWorkflow?: string[], - }, - values: { [key: string]: unknown } + metadataFields: { + merged: string[], + mergedMetadata: MetadataFieldSelected[], + notFound?: string[], + runningWorkflow?: string[], + }, + values: { [key: string]: unknown } }, { dispatch }) => { - const { metadataFields, values } = params; - - let formData = new URLSearchParams(); - formData.append("eventIds", JSON.stringify(metadataFields.merged)); - let metadata : { flavor: string, title: string, fields: any[]}[] = [ - { - flavor: "dublincore/episode", - title: "EVENTS.EVENTS.DETAILS.CATALOG.EPISODE", - fields: [], - }, - ]; - - metadataFields.mergedMetadata.forEach((field) => { - if (field.selected) { - let value = values[field.id]; - metadata[0].fields.push({ - ...field, - value: value, - }); - } - }); - - formData.append("metadata", JSON.stringify(metadata)); - - axios - .put("/admin-ng/event/events/metadata", formData, { - headers: { - "Content-Type": "application/x-www-form-urlencoded", - }, - }) - .then((res) => { - console.info(res); - dispatch( - addNotification({type: "success", key: "BULK_METADATA_UPDATE.ALL_EVENTS_UPDATED"}) - ); - }) - .catch((err) => { - console.error(err); - // if an internal server error occurred, then backend sends further information - if (err.status === 500) { - // backend should send data containing further information about occurred internal error - // if this error data is undefined then an unexpected error occurred - if (!err.data) { - dispatch( - addNotification({type: "error", key: "BULK_METADATA_UPDATE.UNEXPECTED_ERROR"}) - ); - } else { - if (err.data.updated && err.data.updated.length === 0) { - dispatch( - addNotification({type: "error", key: "BULK_METADATA_UPDATE.NO_EVENTS_UPDATED"}) - ); - } - if (err.data.updateFailures && err.data.updateFailures.length > 0) { - dispatch( - addNotification({ - type: "warning", - key: "BULK_METADATA_UPDATE.SOME_EVENTS_NOT_UPDATED" - }) - ); - } - if (err.data.notFound && err.data.notFound.length > 0) { - dispatch( - addNotification({ - type: "warning", - key:"BULK_ACTIONS.EDIT_EVENTS_METADATA.REQUEST_ERRORS.NOT_FOUND" - }) - ); - } - } - } else { - dispatch( - addNotification({type: "error", key: "BULK_METADATA_UPDATE.UNEXPECTED_ERROR"}) - ); - } - }); + const { metadataFields, values } = params; + + let formData = new URLSearchParams(); + formData.append("eventIds", JSON.stringify(metadataFields.merged)); + let metadata : { flavor: string, title: string, fields: any[]}[] = [ + { + flavor: "dublincore/episode", + title: "EVENTS.EVENTS.DETAILS.CATALOG.EPISODE", + fields: [], + }, + ]; + + metadataFields.mergedMetadata.forEach((field) => { + if (field.selected) { + let value = values[field.id]; + metadata[0].fields.push({ + ...field, + value: value, + }); + } + }); + + formData.append("metadata", JSON.stringify(metadata)); + + axios + .put("/admin-ng/event/events/metadata", formData, { + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + }) + .then((res) => { + console.info(res); + dispatch( + addNotification({type: "success", key: "BULK_METADATA_UPDATE.ALL_EVENTS_UPDATED"}) + ); + }) + .catch((err) => { + console.error(err); + // if an internal server error occurred, then backend sends further information + if (err.status === 500) { + // backend should send data containing further information about occurred internal error + // if this error data is undefined then an unexpected error occurred + if (!err.data) { + dispatch( + addNotification({type: "error", key: "BULK_METADATA_UPDATE.UNEXPECTED_ERROR"}) + ); + } else { + if (err.data.updated && err.data.updated.length === 0) { + dispatch( + addNotification({type: "error", key: "BULK_METADATA_UPDATE.NO_EVENTS_UPDATED"}) + ); + } + if (err.data.updateFailures && err.data.updateFailures.length > 0) { + dispatch( + addNotification({ + type: "warning", + key: "BULK_METADATA_UPDATE.SOME_EVENTS_NOT_UPDATED" + }) + ); + } + if (err.data.notFound && err.data.notFound.length > 0) { + dispatch( + addNotification({ + type: "warning", + key:"BULK_ACTIONS.EDIT_EVENTS_METADATA.REQUEST_ERRORS.NOT_FOUND" + }) + ); + } + } + } else { + dispatch( + addNotification({type: "error", key: "BULK_METADATA_UPDATE.UNEXPECTED_ERROR"}) + ); + } + }); }); export const postNewEvent = createAppAsyncThunk('events/postNewEvent', async (params: { - values: { - acls: TransformedAcl[], - configuration: { [key: string]: unknown }, - deviceInputs?: string[], - processingWorkflow: string, - repeatOn: string[], - scheduleDurationHours: string, - scheduleDurationMinutes: string, - scheduleEndDate: string, - scheduleEndHour: string, - scheduleEndMinute: string, - scheduleStartDate: string, - scheduleStartHour: string, - scheduleStartMinute: string, - sourceMode: string, - uploadAssetsTrack?: UploadAssetsTrack[], - [key: string]: unknown, - }, - metadataInfo: MetadataCatalog, - extendedMetadata: MetadataCatalog[], + values: { + acls: TransformedAcl[], + configuration: { [key: string]: unknown }, + deviceInputs?: string[], + processingWorkflow: string, + repeatOn: string[], + scheduleDurationHours: string, + scheduleDurationMinutes: string, + scheduleEndDate: string, + scheduleEndHour: string, + scheduleEndMinute: string, + scheduleStartDate: string, + scheduleStartHour: string, + scheduleStartMinute: string, + sourceMode: string, + uploadAssetsTrack?: UploadAssetsTrack[], + [key: string]: unknown, + }, + metadataInfo: MetadataCatalog, + extendedMetadata: MetadataCatalog[], }, { dispatch, getState }) => { - const { values, metadataInfo, extendedMetadata } = params; - - // get asset upload options from redux store - const state = getState(); - const uploadAssetOptions = getAssetUploadOptions(state); - - let formData = new FormData(); - let source: { - type: string, - metadata?: { - start: Date, - device: unknown, - inputs: string, - end: Date, - duration: string, - rrule?: string, - } - } | undefined = undefined; - - // prepare metadata provided by user - let metadata = prepareMetadataFieldsForPost( - [metadataInfo], - values - ); - const extendedMetadataCatalogs = prepareMetadataFieldsForPost( - extendedMetadata, - values - ); - - // if source mode is UPLOAD than also put metadata fields of that in metadataFields - if (values.sourceMode === "UPLOAD") { - // set source type UPLOAD - source = { - type: values.sourceMode, - }; - if (sourceMetadata.UPLOAD) { - for (const smetadata of sourceMetadata.UPLOAD.metadata) { - metadata[0].fields = metadata[0].fields.concat({ - id: smetadata.id, - value: values[smetadata.id], - type: smetadata.type, - }); - } - } - } - - // transform date data for post request if source mode is SCHEDULE_* - if ( - values.sourceMode === "SCHEDULE_SINGLE" || - values.sourceMode === "SCHEDULE_MULTIPLE" - ) { - // Get timezone offset - //let offset = getTimezoneOffset(); - - // Prepare start date of event for post - let startDate = new Date(values.scheduleStartDate); - // NOTE: if time zone issues still occur during further testing, try to set times to UTC (-offset) - //startDate.setHours((values.scheduleStartHour - offset), values.scheduleStartMinute, 0, 0); - startDate.setHours( - parseInt(values.scheduleStartHour), - parseInt(values.scheduleStartMinute), - 0, - 0 - ); - - let endDate; - - // Prepare end date of event for post - if (values.sourceMode === "SCHEDULE_SINGLE") { - endDate = new Date(values.scheduleStartDate); - } else { - endDate = new Date(values.scheduleEndDate); - } - // NOTE: if time zone issues still occur during further testing, try to set times to UTC (-offset) - //endDate.setHours((values.scheduleEndHour - offset), values.scheduleEndMinute, 0, 0); - endDate.setHours(parseInt(values.scheduleEndHour), parseInt(values.scheduleEndMinute), 0, 0); - - // transform duration into milliseconds - let duration = - parseInt(values.scheduleDurationHours) * 3600000 + - parseInt(values.scheduleDurationMinutes) * 60000; - - // data about source for post request - source = { - type: values.sourceMode, - metadata: { - start: startDate, - device: values.location, - inputs: !!values.deviceInputs ? values.deviceInputs.join(",") : "", - end: endDate, - duration: duration.toString(), - }, - }; - - if (values.sourceMode === "SCHEDULE_MULTIPLE") { - // assemble an iCalendar RRULE (repetition instruction) for the given user input - let rRule = - "FREQ=WEEKLY;BYDAY=" + - values.repeatOn.join(",") + - ";BYHOUR=" + - startDate.getUTCHours() + - ";BYMINUTE=" + - startDate.getUTCMinutes(); - - if (source.metadata) { - source.metadata.rrule = rRule; - } - } - } - - // information about upload assets options - // need to provide all possible upload asset options independent of source mode/type - let assets: { - workflow: string, - options: UploadAssetOption[], - }= { - workflow: WORKFLOW_UPLOAD_ASSETS_NON_TRACK, - options: [], - }; - - // iterate through possible upload asset options and put them in assets - // if source mode/type is UPLOAD and a file for a asset is uploaded by user than append file to form data - for (let i = 0; uploadAssetOptions.length > i; i++) { - if ( - uploadAssetOptions[i].type === "track" && - values.sourceMode === "UPLOAD" - ) { - let asset = values.uploadAssetsTrack?.find( - (asset) => asset.id === uploadAssetOptions[i].id - ); - if (!!asset && !!asset.file) { - if (asset.multiple) { - for (let j = 0; asset.file.length > j; j++) { - formData.append(asset.id + "." + j, asset.file[j]); - } - } else { - formData.append(asset.id + ".0", asset.file[0]); - } - } - assets.options = assets.options.concat(uploadAssetOptions[i]); - } else { - if ( - !!values[uploadAssetOptions[i].id] && - values.sourceMode === "UPLOAD" - ) { - formData.append( - uploadAssetOptions[i].id + ".0", - values[uploadAssetOptions[i].id] as File - ); - assets.options = assets.options.concat(uploadAssetOptions[i]); - } - } - } - - // prepare access rules provided by user - let access = prepareAccessPolicyRulesForPost(values.acls); - - // prepare configurations for post - let configurationPrepared: { [key: string]: string } = {}; - Object.keys(values.configuration).forEach((config) => { - configurationPrepared[config] = String(values.configuration[config]); - }); - - for (const entry of extendedMetadataCatalogs) { - metadata.push(entry); - } - - formData.append( - "metadata", - JSON.stringify({ - metadata: metadata, - processing: { - workflow: values.processingWorkflow, - configuration: configurationPrepared, - }, - access: access, - source: source, - assets: assets, - }) - ); - - // Process bar notification - var config = { - onUploadProgress: function(progressEvent: AxiosProgressEvent) { - var percentCompleted = progressEvent.total ? (progressEvent.loaded * 100) / progressEvent.total : undefined; - if (percentCompleted) { - dispatch(addNotification({ - id: -42000, - type: "success", - key: "EVENTS_UPLOAD_STARTED", - duration: -1, - parameter: { "progress": percentCompleted.toFixed(2) } - })) - } - if (!percentCompleted || percentCompleted >= 100) { - dispatch(removeNotification(-42000)) - } - }, - headers: { - "Content-Type": "multipart/form-data", - }, - }; - - axios - .post("/admin-ng/event/new", formData, config) - .then((response) => { - console.info(response); - dispatch(addNotification({type: "success", key: "EVENTS_CREATED"})); - }) - .catch((response) => { - console.error(response); - dispatch(addNotification({type: "error", key: "EVENTS_NOT_CREATED"})); - }); + const { values, metadataInfo, extendedMetadata } = params; + + // get asset upload options from redux store + const state = getState(); + const uploadAssetOptions = getAssetUploadOptions(state); + + let formData = new FormData(); + let source: { + type: string, + metadata?: { + start: Date, + device: unknown, + inputs: string, + end: Date, + duration: string, + rrule?: string, + } + } | undefined = undefined; + + // prepare metadata provided by user + let metadata = prepareMetadataFieldsForPost( + [metadataInfo], + values + ); + const extendedMetadataCatalogs = prepareMetadataFieldsForPost( + extendedMetadata, + values + ); + + // if source mode is UPLOAD than also put metadata fields of that in metadataFields + if (values.sourceMode === "UPLOAD") { + // set source type UPLOAD + source = { + type: values.sourceMode, + }; + if (sourceMetadata.UPLOAD) { + for (const smetadata of sourceMetadata.UPLOAD.metadata) { + metadata[0].fields = metadata[0].fields.concat({ + id: smetadata.id, + value: values[smetadata.id], + type: smetadata.type, + }); + } + } + } + + // transform date data for post request if source mode is SCHEDULE_* + if ( + values.sourceMode === "SCHEDULE_SINGLE" || + values.sourceMode === "SCHEDULE_MULTIPLE" + ) { + // Get timezone offset + //let offset = getTimezoneOffset(); + + // Prepare start date of event for post + let startDate = new Date(values.scheduleStartDate); + // NOTE: if time zone issues still occur during further testing, try to set times to UTC (-offset) + //startDate.setHours((values.scheduleStartHour - offset), values.scheduleStartMinute, 0, 0); + startDate.setHours( + parseInt(values.scheduleStartHour), + parseInt(values.scheduleStartMinute), + 0, + 0 + ); + + let endDate; + + // Prepare end date of event for post + if (values.sourceMode === "SCHEDULE_SINGLE") { + endDate = new Date(values.scheduleStartDate); + } else { + endDate = new Date(values.scheduleEndDate); + } + // NOTE: if time zone issues still occur during further testing, try to set times to UTC (-offset) + //endDate.setHours((values.scheduleEndHour - offset), values.scheduleEndMinute, 0, 0); + endDate.setHours(parseInt(values.scheduleEndHour), parseInt(values.scheduleEndMinute), 0, 0); + + // transform duration into milliseconds + let duration = + parseInt(values.scheduleDurationHours) * 3600000 + + parseInt(values.scheduleDurationMinutes) * 60000; + + // data about source for post request + source = { + type: values.sourceMode, + metadata: { + start: startDate, + device: values.location, + inputs: !!values.deviceInputs ? values.deviceInputs.join(",") : "", + end: endDate, + duration: duration.toString(), + }, + }; + + if (values.sourceMode === "SCHEDULE_MULTIPLE") { + // assemble an iCalendar RRULE (repetition instruction) for the given user input + let rRule = + "FREQ=WEEKLY;BYDAY=" + + values.repeatOn.join(",") + + ";BYHOUR=" + + startDate.getUTCHours() + + ";BYMINUTE=" + + startDate.getUTCMinutes(); + + if (source.metadata) { + source.metadata.rrule = rRule; + } + } + } + + // information about upload assets options + // need to provide all possible upload asset options independent of source mode/type + let assets: { + workflow: string, + options: UploadAssetOption[], + }= { + workflow: WORKFLOW_UPLOAD_ASSETS_NON_TRACK, + options: [], + }; + + // iterate through possible upload asset options and put them in assets + // if source mode/type is UPLOAD and a file for a asset is uploaded by user than append file to form data + for (let i = 0; uploadAssetOptions.length > i; i++) { + if ( + uploadAssetOptions[i].type === "track" && + values.sourceMode === "UPLOAD" + ) { + let asset = values.uploadAssetsTrack?.find( + (asset) => asset.id === uploadAssetOptions[i].id + ); + if (!!asset && !!asset.file) { + if (asset.multiple) { + for (let j = 0; asset.file.length > j; j++) { + formData.append(asset.id + "." + j, asset.file[j]); + } + } else { + formData.append(asset.id + ".0", asset.file[0]); + } + } + assets.options = assets.options.concat(uploadAssetOptions[i]); + } else { + if ( + !!values[uploadAssetOptions[i].id] && + values.sourceMode === "UPLOAD" + ) { + formData.append( + uploadAssetOptions[i].id + ".0", + values[uploadAssetOptions[i].id] as File + ); + assets.options = assets.options.concat(uploadAssetOptions[i]); + } + } + } + + // prepare access rules provided by user + let access = prepareAccessPolicyRulesForPost(values.acls); + + // prepare configurations for post + let configurationPrepared: { [key: string]: string } = {}; + Object.keys(values.configuration).forEach((config) => { + configurationPrepared[config] = String(values.configuration[config]); + }); + + for (const entry of extendedMetadataCatalogs) { + metadata.push(entry); + } + + formData.append( + "metadata", + JSON.stringify({ + metadata: metadata, + processing: { + workflow: values.processingWorkflow, + configuration: configurationPrepared, + }, + access: access, + source: source, + assets: assets, + }) + ); + + // Process bar notification + var config = { + onUploadProgress: function(progressEvent: AxiosProgressEvent) { + var percentCompleted = progressEvent.total ? (progressEvent.loaded * 100) / progressEvent.total : undefined; + if (percentCompleted) { + dispatch(addNotification({ + id: -42000, + type: "success", + key: "EVENTS_UPLOAD_STARTED", + duration: -1, + parameter: { "progress": percentCompleted.toFixed(2) } + })) + } + if (!percentCompleted || percentCompleted >= 100) { + dispatch(removeNotification(-42000)) + } + }, + headers: { + "Content-Type": "multipart/form-data", + }, + }; + + axios + .post("/admin-ng/event/new", formData, config) + .then((response) => { + console.info(response); + dispatch(addNotification({type: "success", key: "EVENTS_CREATED"})); + }) + .catch((response) => { + console.error(response); + dispatch(addNotification({type: "error", key: "EVENTS_NOT_CREATED"})); + }); }); // delete event with provided id export const deleteEvent = createAppAsyncThunk('events/deleteEvent', async (id: Event["id"], { dispatch }) => { - // API call for deleting an event - axios - .delete(`/admin-ng/event/${id}`) - .then((res) => { - // add success notification depending on status code - if (res.status === 200) { - dispatch(addNotification({type: "success", key: "EVENT_DELETED"})); - } else { - dispatch(addNotification({type: "success", key: "EVENT_WILL_BE_DELETED"})); - } - }) - .catch((res) => { - // add error notification depending on status code - if (res.status === 401) { - dispatch(addNotification({type: "error", key: "EVENTS_NOT_DELETED_NOT_AUTHORIZED"})); - } else { - dispatch(addNotification({type: "error", key: "EVENTS_NOT_DELETED"})); - } - }); + // API call for deleting an event + axios + .delete(`/admin-ng/event/${id}`) + .then((res) => { + // add success notification depending on status code + if (res.status === 200) { + dispatch(addNotification({type: "success", key: "EVENT_DELETED"})); + } else { + dispatch(addNotification({type: "success", key: "EVENT_WILL_BE_DELETED"})); + } + }) + .catch((res) => { + // add error notification depending on status code + if (res.status === 401) { + dispatch(addNotification({type: "error", key: "EVENTS_NOT_DELETED_NOT_AUTHORIZED"})); + } else { + dispatch(addNotification({type: "error", key: "EVENTS_NOT_DELETED"})); + } + }); }); export const deleteMultipleEvent = createAppAsyncThunk('events/deleteMultipleEvent', async (events: Event[], { dispatch }) => { - let data = []; - - for (const event of events) { - if (event.selected) { - data.push(event.id); - } - } - - axios - .post("/admin-ng/event/deleteEvents", data) - .then((res) => { - console.info(res); - //add success notification - dispatch(addNotification({type: "success", key: "EVENTS_DELETED"})); - }) - .catch((res) => { - console.error(res); - //add error notification - dispatch(addNotification({type: "error", key: "EVENTS_NOT_DELETED"})); - }); + let data = []; + + for (const event of events) { + if (event.selected) { + data.push(event.id); + } + } + + axios + .post("/admin-ng/event/deleteEvents", data) + .then((res) => { + console.info(res); + //add success notification + dispatch(addNotification({type: "success", key: "EVENTS_DELETED"})); + }) + .catch((res) => { + console.error(res); + //add error notification + dispatch(addNotification({type: "error", key: "EVENTS_NOT_DELETED"})); + }); }); export const fetchScheduling = createAppAsyncThunk('events/fetchScheduling', async (params: { - events: Event[], - fetchNewScheduling: boolean, - setFormikValue: (field: string, value: EditedEvents[]) => Promise> + events: Event[], + fetchNewScheduling: boolean, + setFormikValue: (field: string, value: EditedEvents[]) => Promise> }, { getState }) => { - const { events, fetchNewScheduling, setFormikValue } = params; - - let editedEvents = []; - - // Only load schedule info about event, when not loaded before - if (fetchNewScheduling) { - let formData = new FormData(); - - for (const event of events) { - if (event.selected) { - formData.append("eventIds", event.id); - } - } - - formData.append("ignoreNonScheduled", JSON.stringify(true)); - - const response = await axios.post( - "/admin-ng/event/scheduling.json", - formData - ); - - let data = await response.data; - - // transform data for further use - for (const d of data) { - let startDate = new Date(d.start); - let endDate = new Date(d.end); - let event = { - eventId: d.eventId, - title: d.agentConfiguration["event.title"], - changedTitle: d.agentConfiguration["event.title"], - series: !!d.agentConfiguration["event.series"] - ? d.agentConfiguration["event.series"] - : "", - changedSeries: !!d.agentConfiguration["event.series"] - ? d.agentConfiguration["event.series"] - : "", - location: d.agentConfiguration["event.location"], - changedLocation: d.agentConfiguration["event.location"], - deviceInputs: d.agentConfiguration["capture.device.names"], - changedDeviceInputs: d.agentConfiguration[ - "capture.device.names" - ].split(","), - startTimeHour: makeTwoDigits(startDate.getHours()), - changedStartTimeHour: makeTwoDigits(startDate.getHours()), - startTimeMinutes: makeTwoDigits(startDate.getMinutes()), - changedStartTimeMinutes: makeTwoDigits(startDate.getMinutes()), - endTimeHour: makeTwoDigits(endDate.getHours()), - changedEndTimeHour: makeTwoDigits(endDate.getHours()), - endTimeMinutes: makeTwoDigits(endDate.getMinutes()), - changedEndTimeMinutes: makeTwoDigits(endDate.getMinutes()), - weekday: weekdays[(startDate.getDay() + 6) % 7].name, - changedWeekday: weekdays[(startDate.getDay() + 6) % 7].name, - }; - editedEvents.push(event); - } - } else { - const state = getState(); - editedEvents = getSchedulingEditedEvents(state); - } - - const responseSeriesOptions = await fetchSeriesOptions(); - - setFormikValue("editedEvents", editedEvents); - - return { editedEvents, responseSeriesOptions } + const { events, fetchNewScheduling, setFormikValue } = params; + + let editedEvents = []; + + // Only load schedule info about event, when not loaded before + if (fetchNewScheduling) { + let formData = new FormData(); + + for (const event of events) { + if (event.selected) { + formData.append("eventIds", event.id); + } + } + + formData.append("ignoreNonScheduled", JSON.stringify(true)); + + const response = await axios.post( + "/admin-ng/event/scheduling.json", + formData + ); + + let data = await response.data; + + // transform data for further use + for (const d of data) { + let startDate = new Date(d.start); + let endDate = new Date(d.end); + let event = { + eventId: d.eventId, + title: d.agentConfiguration["event.title"], + changedTitle: d.agentConfiguration["event.title"], + series: !!d.agentConfiguration["event.series"] + ? d.agentConfiguration["event.series"] + : "", + changedSeries: !!d.agentConfiguration["event.series"] + ? d.agentConfiguration["event.series"] + : "", + location: d.agentConfiguration["event.location"], + changedLocation: d.agentConfiguration["event.location"], + deviceInputs: d.agentConfiguration["capture.device.names"], + changedDeviceInputs: d.agentConfiguration[ + "capture.device.names" + ].split(","), + startTimeHour: makeTwoDigits(startDate.getHours()), + changedStartTimeHour: makeTwoDigits(startDate.getHours()), + startTimeMinutes: makeTwoDigits(startDate.getMinutes()), + changedStartTimeMinutes: makeTwoDigits(startDate.getMinutes()), + endTimeHour: makeTwoDigits(endDate.getHours()), + changedEndTimeHour: makeTwoDigits(endDate.getHours()), + endTimeMinutes: makeTwoDigits(endDate.getMinutes()), + changedEndTimeMinutes: makeTwoDigits(endDate.getMinutes()), + weekday: weekdays[(startDate.getDay() + 6) % 7].name, + changedWeekday: weekdays[(startDate.getDay() + 6) % 7].name, + }; + editedEvents.push(event); + } + } else { + const state = getState(); + editedEvents = getSchedulingEditedEvents(state); + } + + const responseSeriesOptions = await fetchSeriesOptions(); + + setFormikValue("editedEvents", editedEvents); + + return { editedEvents, responseSeriesOptions } }); // update multiple scheduled events at once export const updateScheduledEventsBulk = createAppAsyncThunk('events/updateScheduledEventsBulk', async ( - values: { - changedEvents: string[], - editedEvents: EditedEvents[], - events: Event[], - }, + values: { + changedEvents: string[], + editedEvents: EditedEvents[], + events: Event[], + }, { dispatch }) => { - let formData = new FormData(); - let update = []; - let timezone = moment.tz.guess(); - - for (const changedEvent of values.changedEvents) { - let eventChanges = values.editedEvents.find( - (event) => event.eventId === changedEvent - ); - let originalEvent = values.events.find( - (event) => event.id === changedEvent - ); - - if (!eventChanges || !originalEvent) { - dispatch( - addNotification({ - type: "error", - key: "EVENTS_NOT_UPDATED_ID", - duration: 10, - parameter: { id: changedEvent } - }) - ); - return; - } - - update.push({ - events: [eventChanges.eventId], - metadata: { - // flavor: originalEvent.flavor, - title: "EVENTS.EVENTS.DETAILS.CATALOG.EPISODE", - fields: [ - { - id: "title", - label: "EVENTS.EVENTS.DETAILS.METADATA.TITLE", - readOnly: false, - required: false, - type: "text", - value: eventChanges.changedTitle, - }, - { - id: "isPartOf", - collection: {}, - label: "EVENTS.EVENTS.DETAILS.METADATA.SERIES", - readOnly: false, - required: false, - translatable: false, - type: "text", - value: eventChanges.changedSeries, - }, - ], - }, - scheduling: { - timezone: timezone, - start: { - hour: parseInt(eventChanges.changedStartTimeHour), - minute: parseInt(eventChanges.changedStartTimeMinutes), - }, - end: { - hour: parseInt(eventChanges.changedEndTimeHour), - minute: parseInt(eventChanges.changedEndTimeMinutes), - }, - weekday: eventChanges.changedWeekday, - agentId: eventChanges.changedLocation, - // the following two lines can be commented in, when the possibility of a selection of individual inputs is desired and the backend has been adapted to support it (the word inputs may have to be replaced accordingly) - //, - //inputs: eventChanges.changedDeviceInputs.join(',') - }, - }); - } - - formData.append("update", JSON.stringify(update)); - - axios - .put("/admin-ng/event/bulk/update", formData) - .then((res) => { - console.info(res); - dispatch(addNotification({type: "success", key: "EVENTS_UPDATED_ALL"})); - }) - .catch((res) => { - console.error(res); - dispatch(addNotification({type: "error", key: "EVENTS_NOT_UPDATED_ALL"})); - }); + let formData = new FormData(); + let update = []; + let timezone = moment.tz.guess(); + + for (const changedEvent of values.changedEvents) { + let eventChanges = values.editedEvents.find( + (event) => event.eventId === changedEvent + ); + let originalEvent = values.events.find( + (event) => event.id === changedEvent + ); + + if (!eventChanges || !originalEvent) { + dispatch( + addNotification({ + type: "error", + key: "EVENTS_NOT_UPDATED_ID", + duration: 10, + parameter: { id: changedEvent } + }) + ); + return; + } + + update.push({ + events: [eventChanges.eventId], + metadata: { + // flavor: originalEvent.flavor, + title: "EVENTS.EVENTS.DETAILS.CATALOG.EPISODE", + fields: [ + { + id: "title", + label: "EVENTS.EVENTS.DETAILS.METADATA.TITLE", + readOnly: false, + required: false, + type: "text", + value: eventChanges.changedTitle, + }, + { + id: "isPartOf", + collection: {}, + label: "EVENTS.EVENTS.DETAILS.METADATA.SERIES", + readOnly: false, + required: false, + translatable: false, + type: "text", + value: eventChanges.changedSeries, + }, + ], + }, + scheduling: { + timezone: timezone, + start: { + hour: parseInt(eventChanges.changedStartTimeHour), + minute: parseInt(eventChanges.changedStartTimeMinutes), + }, + end: { + hour: parseInt(eventChanges.changedEndTimeHour), + minute: parseInt(eventChanges.changedEndTimeMinutes), + }, + weekday: eventChanges.changedWeekday, + agentId: eventChanges.changedLocation, + // the following two lines can be commented in, when the possibility of a selection of individual inputs is desired and the backend has been adapted to support it (the word inputs may have to be replaced accordingly) + //, + //inputs: eventChanges.changedDeviceInputs.join(',') + }, + }); + } + + formData.append("update", JSON.stringify(update)); + + axios + .put("/admin-ng/event/bulk/update", formData) + .then((res) => { + console.info(res); + dispatch(addNotification({type: "success", key: "EVENTS_UPDATED_ALL"})); + }) + .catch((res) => { + console.error(res); + dispatch(addNotification({type: "error", key: "EVENTS_NOT_UPDATED_ALL"})); + }); }); // check provided date range for conflicts export const checkConflicts = (values: { - location: string, - repeatOn: string[], - scheduleDurationHours: string, - scheduleDurationMinutes: string, - scheduleEndDate: string, - scheduleEndHour: string, - scheduleEndMinute: string, - scheduleStartDate: string, - scheduleStartHour: string, - scheduleStartMinute: string, - sourceMode: string, + location: string, + repeatOn: string[], + scheduleDurationHours: string, + scheduleDurationMinutes: string, + scheduleEndDate: string, + scheduleEndHour: string, + scheduleEndMinute: string, + scheduleStartDate: string, + scheduleStartHour: string, + scheduleStartMinute: string, + sourceMode: string, }) => async (dispatch: AppDispatch) => { - let check = true; - - // Only perform checks if source mode is SCHEDULE_SINGLE or SCHEDULE_MULTIPLE - if ( - values.sourceMode === "SCHEDULE_SINGLE" || - values.sourceMode === "SCHEDULE_MULTIPLE" - ) { - // Get timezone offset; Checks should be performed on UTC times - // let offset = getTimezoneOffset(); - - // Prepare start date of event for check - let startDate = new Date(values.scheduleStartDate); - // NOTE: if time zone issues still occur during further testing, try to set times to UTC (-offset) - startDate.setHours( - parseInt(values.scheduleStartHour), - parseInt(values.scheduleStartMinute), - 0, - 0 - ); - - // If start date of event is smaller than today --> Event is in past - if (values.sourceMode === "SCHEDULE_SINGLE" && startDate < new Date()) { - dispatch( - addNotification({ - type: "error", - key: "CONFLICT_ALREADY_ENDED", - duration: -1, - parameter: undefined, - context: NOTIFICATION_CONTEXT - }) - ); - check = false; - } - - const endDate = new Date(values.scheduleEndDate); - // NOTE: if time zone issues still occur during further testing, try to set times to UTC (-offset) - endDate.setHours(parseInt(values.scheduleEndHour), parseInt(values.scheduleEndMinute), 0, 0); - - // if start date is higher than end date --> end date is before start date - if (startDate > endDate) { - dispatch( - addNotification({ - type: "error", - key: "CONFLICT_END_BEFORE_START", - duration: -1, - parameter: undefined, - context: NOTIFICATION_CONTEXT - }) - ); - check = false; - } - - // transform duration into milliseconds (needed for API request) - let duration = - parseInt(values.scheduleDurationHours) * 3600000 + - parseInt(values.scheduleDurationMinutes) * 60000; - - // Check for conflicts with other already scheduled events - let conflicts = - values.sourceMode === "SCHEDULE_SINGLE" - ? await checkForConflicts(startDate, endDate, duration, values.location) - : await checkForConflicts( - startDate, - endDate, - duration, - values.location, - values.repeatOn - ); - - // If conflicts with already scheduled events detected --> need to change times/date - if (conflicts && conflicts.length > 0) { - dispatch( - addNotification({ - type: "error", - key: "CONFLICT_DETECTED", - duration: -1, - parameter: undefined, - context: NOTIFICATION_CONTEXT - }) - ); - check = false; - return conflicts; - } - } - return check; + let check = true; + + // Only perform checks if source mode is SCHEDULE_SINGLE or SCHEDULE_MULTIPLE + if ( + values.sourceMode === "SCHEDULE_SINGLE" || + values.sourceMode === "SCHEDULE_MULTIPLE" + ) { + // Get timezone offset; Checks should be performed on UTC times + // let offset = getTimezoneOffset(); + + // Prepare start date of event for check + let startDate = new Date(values.scheduleStartDate); + // NOTE: if time zone issues still occur during further testing, try to set times to UTC (-offset) + startDate.setHours( + parseInt(values.scheduleStartHour), + parseInt(values.scheduleStartMinute), + 0, + 0 + ); + + // If start date of event is smaller than today --> Event is in past + if (values.sourceMode === "SCHEDULE_SINGLE" && startDate < new Date()) { + dispatch( + addNotification({ + type: "error", + key: "CONFLICT_ALREADY_ENDED", + duration: -1, + parameter: undefined, + context: NOTIFICATION_CONTEXT + }) + ); + check = false; + } + + const endDate = new Date(values.scheduleEndDate); + // NOTE: if time zone issues still occur during further testing, try to set times to UTC (-offset) + endDate.setHours(parseInt(values.scheduleEndHour), parseInt(values.scheduleEndMinute), 0, 0); + + // if start date is higher than end date --> end date is before start date + if (startDate > endDate) { + dispatch( + addNotification({ + type: "error", + key: "CONFLICT_END_BEFORE_START", + duration: -1, + parameter: undefined, + context: NOTIFICATION_CONTEXT + }) + ); + check = false; + } + + // transform duration into milliseconds (needed for API request) + let duration = + parseInt(values.scheduleDurationHours) * 3600000 + + parseInt(values.scheduleDurationMinutes) * 60000; + + // Check for conflicts with other already scheduled events + let conflicts = + values.sourceMode === "SCHEDULE_SINGLE" + ? await checkForConflicts(startDate, endDate, duration, values.location) + : await checkForConflicts( + startDate, + endDate, + duration, + values.location, + values.repeatOn + ); + + // If conflicts with already scheduled events detected --> need to change times/date + if (conflicts && conflicts.length > 0) { + dispatch( + addNotification({ + type: "error", + key: "CONFLICT_DETECTED", + duration: -1, + parameter: undefined, + context: NOTIFICATION_CONTEXT + }) + ); + check = false; + return conflicts; + } + } + return check; }; // Check for conflicts with already scheduled events export const checkForConflicts = async ( - startDate: Date, - endDate: Date, - duration: number, - device: string, - repeatOn: string[] | undefined = undefined + startDate: Date, + endDate: Date, + duration: number, + device: string, + repeatOn: string[] | undefined = undefined ) => { - let metadata = !!repeatOn - ? { - start: startDate, - device: device, - duration: duration.toString(), - end: endDate, - rrule: `FREQ=WEEKLY;BYDAY=${repeatOn.join()};BYHOUR=${startDate.getUTCHours()};BYMINUTE=${startDate.getUTCMinutes()}`, - } - : { - start: startDate, - device: device, - duration: duration.toString(), - end: endDate, - }; - let status = 0; - - let formData = new URLSearchParams(); - formData.append("metadata", JSON.stringify(metadata)); - - return await axios - .post("/admin-ng/event/new/conflicts", formData, { - headers: { - "Content-Type": "application/x-www-form-urlencoded", - }, - }) - .then((response) => { - status = response.status; - const conflicts = []; - if (status === 409) { - const conflictsResponse = response.data; - - for (const conflict of conflictsResponse) { - conflicts.push({ - title: conflict.title, - start: conflict.start, - end: conflict.end, - }); - } - } - return conflicts; - }) - .catch((reason) => { - status = reason.response.status; - const conflicts = []; - if (status === 409) { - const conflictsResponse = reason.response.data; - - for (const conflict of conflictsResponse) { - conflicts.push({ - title: conflict.title, - start: conflict.start, - end: conflict.end, - }); - } - } - return conflicts; - }); + let metadata = !!repeatOn + ? { + start: startDate, + device: device, + duration: duration.toString(), + end: endDate, + rrule: `FREQ=WEEKLY;BYDAY=${repeatOn.join()};BYHOUR=${startDate.getUTCHours()};BYMINUTE=${startDate.getUTCMinutes()}`, + } + : { + start: startDate, + device: device, + duration: duration.toString(), + end: endDate, + }; + let status = 0; + + let formData = new URLSearchParams(); + formData.append("metadata", JSON.stringify(metadata)); + + return await axios + .post("/admin-ng/event/new/conflicts", formData, { + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + }) + .then((response) => { + status = response.status; + const conflicts = []; + if (status === 409) { + const conflictsResponse = response.data; + + for (const conflict of conflictsResponse) { + conflicts.push({ + title: conflict.title, + start: conflict.start, + end: conflict.end, + }); + } + } + return conflicts; + }) + .catch((reason) => { + status = reason.response.status; + const conflicts = []; + if (status === 409) { + const conflictsResponse = reason.response.data; + + for (const conflict of conflictsResponse) { + conflicts.push({ + title: conflict.title, + start: conflict.start, + end: conflict.end, + }); + } + } + return conflicts; + }); }; // check if there are any scheduling conflicts with other events export const checkForSchedulingConflicts = (events: EditedEvents[]) => async (dispatch: AppDispatch) => { - const formData = new FormData(); - let update = []; - let timezone = moment.tz.guess(); - for (const event of events) { - update.push({ - events: [event.eventId], - scheduling: { - timezone: timezone, - start: { - hour: parseInt(event.changedStartTimeHour), - minute: parseInt(event.changedStartTimeMinutes), - }, - end: { - hour: parseInt(event.changedEndTimeHour), - minutes: parseInt(event.changedEndTimeMinutes), - }, - weekday: event.changedWeekday, - agentId: event.changedLocation, - }, - }); - } - - formData.append("update", JSON.stringify(update)); - - let data: Conflict[] = []; - - axios - .post("/admin-ng/event/bulk/conflicts", formData) - .then((res) => console.info(res)) - .catch((res) => { - if (res.response.status === 409) { - dispatch( - addNotification({ - type: "error", - key: "CONFLICT_BULK_DETECTED", - duration: -1, - parameter: undefined, - context: NOTIFICATION_CONTEXT - }) - ); - data = res.response.data; - } - console.error(res); - }); - - return data; + const formData = new FormData(); + let update = []; + let timezone = moment.tz.guess(); + for (const event of events) { + update.push({ + events: [event.eventId], + scheduling: { + timezone: timezone, + start: { + hour: parseInt(event.changedStartTimeHour), + minute: parseInt(event.changedStartTimeMinutes), + }, + end: { + hour: parseInt(event.changedEndTimeHour), + minutes: parseInt(event.changedEndTimeMinutes), + }, + weekday: event.changedWeekday, + agentId: event.changedLocation, + }, + }); + } + + formData.append("update", JSON.stringify(update)); + + let data: Conflict[] = []; + + axios + .post("/admin-ng/event/bulk/conflicts", formData) + .then((res) => console.info(res)) + .catch((res) => { + if (res.response.status === 409) { + dispatch( + addNotification({ + type: "error", + key: "CONFLICT_BULK_DETECTED", + duration: -1, + parameter: undefined, + context: NOTIFICATION_CONTEXT + }) + ); + data = res.response.data; + } + console.error(res); + }); + + return data; }; const eventSlice = createSlice({ - name: 'events', - initialState, - reducers: { - setEventColumns(state, action: PayloadAction< - EventState["columns"] - >) { - state.columns = action.payload; - }, - setShowActions(state, action: PayloadAction< - EventState["showActions"] - >) { - state.showActions = action.payload; - }, - }, - // These are used for thunks - extraReducers: builder => { - builder - .addCase(fetchEvents.pending, (state) => { - state.status = 'loading'; - }) - .addCase(fetchEvents.fulfilled, (state, action: PayloadAction<{ - total: EventState["total"], - count: EventState["count"], - limit: EventState["limit"], - offset: EventState["offset"], - results: EventState["results"], - }>) => { - state.status = 'succeeded'; - const events = action.payload; - state.total = events.total; - state.count = events.count; - state.limit = events.limit; - state.offset = events.offset; - state.results = events.results; - }) - .addCase(fetchEvents.rejected, (state, action) => { - state.status = 'failed'; - state.results = []; - state.error = action.error; - }) - .addCase(fetchEventMetadata.pending, (state) => { - state.statusMetadata = 'loading'; - }) - .addCase(fetchEventMetadata.fulfilled, (state, action: PayloadAction<{ - metadata: EventState["metadata"], - extendedMetadata: EventState["extendedMetadata"], - }>) => { - state.statusMetadata = 'succeeded'; - const eventMetadata = action.payload; - state.metadata = eventMetadata.metadata; - state.extendedMetadata = eventMetadata.extendedMetadata; - }) - .addCase(fetchEventMetadata.rejected, (state, action) => { - state.statusMetadata = 'failed'; - state.extendedMetadata = []; - state.errorMetadata = action.error; - }) - .addCase(fetchScheduling.pending, (state) => { - state.statusSchedulingInfo = 'loading'; - }) - .addCase(fetchScheduling.fulfilled, (state, action: PayloadAction<{ - editedEvents: EventState["schedulingInfo"]["editedEvents"], - responseSeriesOptions: EventState["schedulingInfo"]["seriesOptions"], - }>) => { - state.statusSchedulingInfo = 'succeeded'; - const schedulingInfo = action.payload; - state.schedulingInfo.editedEvents = schedulingInfo.editedEvents; - state.schedulingInfo.seriesOptions = schedulingInfo.responseSeriesOptions; - }) - .addCase(fetchScheduling.rejected, (state, action) => { - state.statusSchedulingInfo = 'failed'; - state.schedulingInfo.editedEvents = []; - state.errorSchedulingInfo = action.error; - }) - .addCase(fetchAssetUploadOptions.pending, (state) => { - state.statusAssetUploadOptions = 'loading'; - }) - .addCase(fetchAssetUploadOptions.fulfilled, (state, action: PayloadAction<{ - workflow: EventState["uploadAssetWorkflow"], - newAssetUploadOptions: EventState["uploadAssetOptions"], - } | undefined>) => { - state.statusAssetUploadOptions = 'succeeded'; - const assetUpload = action.payload; - if (assetUpload) { - state.uploadAssetWorkflow = assetUpload.workflow; - state.uploadAssetOptions = assetUpload.newAssetUploadOptions; - } - }) - .addCase(fetchAssetUploadOptions.rejected, (state, action) => { - state.statusAssetUploadOptions = 'failed'; - state.schedulingInfo.editedEvents = []; - state.errorAssetUploadOptions = action.error; - }); - } + name: 'events', + initialState, + reducers: { + setEventColumns(state, action: PayloadAction< + EventState["columns"] + >) { + state.columns = action.payload; + }, + setShowActions(state, action: PayloadAction< + EventState["showActions"] + >) { + state.showActions = action.payload; + }, + }, + // These are used for thunks + extraReducers: builder => { + builder + .addCase(fetchEvents.pending, (state) => { + state.status = 'loading'; + }) + .addCase(fetchEvents.fulfilled, (state, action: PayloadAction<{ + total: EventState["total"], + count: EventState["count"], + limit: EventState["limit"], + offset: EventState["offset"], + results: EventState["results"], + }>) => { + state.status = 'succeeded'; + const events = action.payload; + state.total = events.total; + state.count = events.count; + state.limit = events.limit; + state.offset = events.offset; + state.results = events.results; + }) + .addCase(fetchEvents.rejected, (state, action) => { + state.status = 'failed'; + state.results = []; + state.error = action.error; + }) + .addCase(fetchEventMetadata.pending, (state) => { + state.statusMetadata = 'loading'; + }) + .addCase(fetchEventMetadata.fulfilled, (state, action: PayloadAction<{ + metadata: EventState["metadata"], + extendedMetadata: EventState["extendedMetadata"], + }>) => { + state.statusMetadata = 'succeeded'; + const eventMetadata = action.payload; + state.metadata = eventMetadata.metadata; + state.extendedMetadata = eventMetadata.extendedMetadata; + }) + .addCase(fetchEventMetadata.rejected, (state, action) => { + state.statusMetadata = 'failed'; + state.extendedMetadata = []; + state.errorMetadata = action.error; + }) + .addCase(fetchScheduling.pending, (state) => { + state.statusSchedulingInfo = 'loading'; + }) + .addCase(fetchScheduling.fulfilled, (state, action: PayloadAction<{ + editedEvents: EventState["schedulingInfo"]["editedEvents"], + responseSeriesOptions: EventState["schedulingInfo"]["seriesOptions"], + }>) => { + state.statusSchedulingInfo = 'succeeded'; + const schedulingInfo = action.payload; + state.schedulingInfo.editedEvents = schedulingInfo.editedEvents; + state.schedulingInfo.seriesOptions = schedulingInfo.responseSeriesOptions; + }) + .addCase(fetchScheduling.rejected, (state, action) => { + state.statusSchedulingInfo = 'failed'; + state.schedulingInfo.editedEvents = []; + state.errorSchedulingInfo = action.error; + }) + .addCase(fetchAssetUploadOptions.pending, (state) => { + state.statusAssetUploadOptions = 'loading'; + }) + .addCase(fetchAssetUploadOptions.fulfilled, (state, action: PayloadAction<{ + workflow: EventState["uploadAssetWorkflow"], + newAssetUploadOptions: EventState["uploadAssetOptions"], + } | undefined>) => { + state.statusAssetUploadOptions = 'succeeded'; + const assetUpload = action.payload; + if (assetUpload) { + state.uploadAssetWorkflow = assetUpload.workflow; + state.uploadAssetOptions = assetUpload.newAssetUploadOptions; + } + }) + .addCase(fetchAssetUploadOptions.rejected, (state, action) => { + state.statusAssetUploadOptions = 'failed'; + state.schedulingInfo.editedEvents = []; + state.errorAssetUploadOptions = action.error; + }); + } }); export const { - setEventColumns, - setShowActions, + setEventColumns, + setShowActions, } = eventSlice.actions; // Export the slice reducer as the default export diff --git a/src/slices/groupDetailsSlice.ts b/src/slices/groupDetailsSlice.ts index 407a748ada..b02a679db4 100644 --- a/src/slices/groupDetailsSlice.ts +++ b/src/slices/groupDetailsSlice.ts @@ -10,12 +10,12 @@ import { Group } from './groupSlice'; */ export type GroupDetails = Group & { - roles: string[], + roles: string[], } export type GroupDetailsState = GroupDetails & { - status: 'uninitialized' | 'loading' | 'succeeded' | 'failed', - error: SerializedError | null, + status: 'uninitialized' | 'loading' | 'succeeded' | 'failed', + error: SerializedError | null, } export interface UpdateGroupDetailsState extends Omit { @@ -24,108 +24,108 @@ export interface UpdateGroupDetailsState extends Omit { - const res = await axios.get(`/admin-ng/groups/${groupId}`); - const response = await res.data; + const res = await axios.get(`/admin-ng/groups/${groupId}`); + const response = await res.data; - let users: GroupDetailsState["users"] = []; - if (response.users.length > 0) { - users = response.users.map((user: { username: string, name: string }) => { - return { - id: user.username, - name: user.name, - }; - }); - } + let users: GroupDetailsState["users"] = []; + if (response.users.length > 0) { + users = response.users.map((user: { username: string, name: string }) => { + return { + id: user.username, + name: user.name, + }; + }); + } - const groupDetails = { - role: response.role, - roles: response.roles, - name: response.name, - description: response.description, - id: response.id, - users: users, - }; + const groupDetails = { + role: response.role, + roles: response.roles, + name: response.name, + description: response.description, + id: response.id, + users: users, + }; - return groupDetails; + return groupDetails; }); // update details of a certain group export const updateGroupDetails = createAppAsyncThunk('groupDetails/updateGroupDetails', async (params: { - values: UpdateGroupDetailsState, - groupId: GroupDetails["id"] + values: UpdateGroupDetailsState, + groupId: GroupDetails["id"] }, {dispatch}) => { - const { values, groupId } = params + const { values, groupId } = params - // get URL params used for put request - let data = buildGroupBody(values); + // get URL params used for put request + let data = buildGroupBody(values); - // PUT request - axios - .put(`/admin-ng/groups/${groupId}`, data) - .then((response) => { - console.info(response); - dispatch(addNotification({type: "success", key: "GROUP_UPDATED"})); - }) - .catch((response) => { - console.error(response); - if (response.status === 409) { - dispatch(addNotification({type: "error", key: "GROUP_CONFLICT"})); - } else { - dispatch(addNotification({type: "error", key: "GROUP_NOT_SAVED"})); - } - }); + // PUT request + axios + .put(`/admin-ng/groups/${groupId}`, data) + .then((response) => { + console.info(response); + dispatch(addNotification({type: "success", key: "GROUP_UPDATED"})); + }) + .catch((response) => { + console.error(response); + if (response.status === 409) { + dispatch(addNotification({type: "error", key: "GROUP_CONFLICT"})); + } else { + dispatch(addNotification({type: "error", key: "GROUP_NOT_SAVED"})); + } + }); }); const groupDetailsSlice = createSlice({ - name: 'groupDetails', - initialState, - reducers: {}, - // These are used for thunks - extraReducers: builder => { - builder - .addCase(fetchGroupDetails.pending, (state) => { - state.status = 'loading'; - }) - .addCase(fetchGroupDetails.fulfilled, (state, action: PayloadAction<{ - role: GroupDetailsState["role"], - roles: GroupDetailsState["roles"], - name: GroupDetailsState["name"], - description: GroupDetailsState["description"], - id: GroupDetailsState["id"], - users: GroupDetailsState["users"], - }>) => { - state.status = 'succeeded'; - const groupDetails = action.payload; - state.role = groupDetails.role; - state.roles = groupDetails.roles; - state.name = groupDetails.name; - state.description = groupDetails.description; - state.id = groupDetails.id; - state.users = groupDetails.users; - }) - .addCase(fetchGroupDetails.rejected, (state, action) => { - state.status = 'failed'; - state.role = ""; - state.roles = []; - state.name = ""; - state.description = ""; - state.id = ""; - state.users = []; - state.error = action.error; - }); - } + name: 'groupDetails', + initialState, + reducers: {}, + // These are used for thunks + extraReducers: builder => { + builder + .addCase(fetchGroupDetails.pending, (state) => { + state.status = 'loading'; + }) + .addCase(fetchGroupDetails.fulfilled, (state, action: PayloadAction<{ + role: GroupDetailsState["role"], + roles: GroupDetailsState["roles"], + name: GroupDetailsState["name"], + description: GroupDetailsState["description"], + id: GroupDetailsState["id"], + users: GroupDetailsState["users"], + }>) => { + state.status = 'succeeded'; + const groupDetails = action.payload; + state.role = groupDetails.role; + state.roles = groupDetails.roles; + state.name = groupDetails.name; + state.description = groupDetails.description; + state.id = groupDetails.id; + state.users = groupDetails.users; + }) + .addCase(fetchGroupDetails.rejected, (state, action) => { + state.status = 'failed'; + state.role = ""; + state.roles = []; + state.name = ""; + state.description = ""; + state.id = ""; + state.users = []; + state.error = action.error; + }); + } }); // export const {} = groupDetailsSlice.actions; diff --git a/src/slices/groupSlice.ts b/src/slices/groupSlice.ts index e668fc53a0..fdaac87e79 100644 --- a/src/slices/groupSlice.ts +++ b/src/slices/groupSlice.ts @@ -11,131 +11,131 @@ import { initialFormValuesNewGroup } from '../configs/modalConfig'; * This file contains redux reducer for actions affecting the state of groups */ export type Group = { - description: string, - id: string, - name: string, + description: string, + id: string, + name: string, role: string, - users: {id: string, name: string}[], + users: {id: string, name: string}[], } type GroupState = { - status: 'uninitialized' | 'loading' | 'succeeded' | 'failed', - error: SerializedError | null, - results: Group[], - columns: TableConfig["columns"], - total: number, - count: number, - offset: number, - limit: number, + status: 'uninitialized' | 'loading' | 'succeeded' | 'failed', + error: SerializedError | null, + results: Group[], + columns: TableConfig["columns"], + total: number, + count: number, + offset: number, + limit: number, } // Fill columns initially with columns defined in groupsTableConfig const initialColumns = groupsTableConfig.columns.map((column) => ({ - ...column, - deactivated: false, + ...column, + deactivated: false, })); // Initial state of groups in redux store const initialState: GroupState = { - status: 'uninitialized', - error: null, - results: [], - columns: initialColumns, - total: 0, - count: 0, - offset: 0, - limit: 0, + status: 'uninitialized', + error: null, + results: [], + columns: initialColumns, + total: 0, + count: 0, + offset: 0, + limit: 0, }; // fetch groups from server export const fetchGroups = createAppAsyncThunk('groups/fetchGroups', async (_, { getState }) => { - const state = getState(); - let params = getURLParams(state, "groups"); - // Just make the async request here, and return the response. - // This will automatically dispatch a `pending` action first, - // and then `fulfilled` or `rejected` actions based on the promise. - const res = await axios.get("/admin-ng/groups/groups.json", { params: params }); - return res.data; + const state = getState(); + let params = getURLParams(state, "groups"); + // Just make the async request here, and return the response. + // This will automatically dispatch a `pending` action first, + // and then `fulfilled` or `rejected` actions based on the promise. + const res = await axios.get("/admin-ng/groups/groups.json", { params: params }); + return res.data; }); // post new group to backend export const postNewGroup = createAppAsyncThunk('groups/postNewGroup', async (values: typeof initialFormValuesNewGroup, {dispatch}) => { - // get URL params used for post request - let data = buildGroupBody(values); + // get URL params used for post request + let data = buildGroupBody(values); - // POST request - axios - .post("/admin-ng/groups", data, { - headers: { - "Content-Type": "application/x-www-form-urlencoded", - }, - }) - .then((response) => { - console.info(response); - dispatch(addNotification({type: "success", key: "GROUP_ADDED"})); - }) - .catch((response) => { - console.error(response); - if (response.status === 409) { - dispatch(addNotification({type: "error", key: "GROUP_CONFLICT"})); - } else { - dispatch(addNotification({type: "error", key: "GROUP_NOT_SAVED"})); - } - }); + // POST request + axios + .post("/admin-ng/groups", data, { + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + }) + .then((response) => { + console.info(response); + dispatch(addNotification({type: "success", key: "GROUP_ADDED"})); + }) + .catch((response) => { + console.error(response); + if (response.status === 409) { + dispatch(addNotification({type: "error", key: "GROUP_CONFLICT"})); + } else { + dispatch(addNotification({type: "error", key: "GROUP_NOT_SAVED"})); + } + }); }); export const deleteGroup = createAppAsyncThunk('groups/deleteGroup', async (id: Group["id"], {dispatch}) => { - // API call for deleting a group - axios - .delete(`/admin-ng/groups/${id}`) - .then((res) => { - console.info(res); - // add success notification - dispatch(addNotification({type: "success", key: "GROUP_DELETED"})); - }) - .catch((res) => { - console.error(res); - // add error notification - dispatch(addNotification({type: "error", key: "GROUP_NOT_DELETED"})); - }); + // API call for deleting a group + axios + .delete(`/admin-ng/groups/${id}`) + .then((res) => { + console.info(res); + // add success notification + dispatch(addNotification({type: "success", key: "GROUP_DELETED"})); + }) + .catch((res) => { + console.error(res); + // add error notification + dispatch(addNotification({type: "error", key: "GROUP_NOT_DELETED"})); + }); }); const groupSlice = createSlice({ - name: 'groups', - initialState, - reducers: { - setGroupColumns(state, action: PayloadAction< - GroupState["columns"] - >) { - state.columns = action.payload; - }, - }, - // These are used for thunks - extraReducers: builder => { - builder - .addCase(fetchGroups.pending, (state) => { - state.status = 'loading'; - }) - .addCase(fetchGroups.fulfilled, (state, action: PayloadAction<{ - total: GroupState["total"], - count: GroupState["count"], - limit: GroupState["limit"], - offset: GroupState["offset"], - results: GroupState["results"], - }>) => { - state.status = 'succeeded'; - const groups = action.payload; - state.total = groups.total; - state.count = groups.count; - state.limit = groups.limit; - state.offset = groups.offset; - state.results = groups.results; - }) - .addCase(fetchGroups.rejected, (state, action) => { - state.status = 'failed'; - state.error = action.error; - }); - } + name: 'groups', + initialState, + reducers: { + setGroupColumns(state, action: PayloadAction< + GroupState["columns"] + >) { + state.columns = action.payload; + }, + }, + // These are used for thunks + extraReducers: builder => { + builder + .addCase(fetchGroups.pending, (state) => { + state.status = 'loading'; + }) + .addCase(fetchGroups.fulfilled, (state, action: PayloadAction<{ + total: GroupState["total"], + count: GroupState["count"], + limit: GroupState["limit"], + offset: GroupState["offset"], + results: GroupState["results"], + }>) => { + state.status = 'succeeded'; + const groups = action.payload; + state.total = groups.total; + state.count = groups.count; + state.limit = groups.limit; + state.offset = groups.offset; + state.results = groups.results; + }) + .addCase(fetchGroups.rejected, (state, action) => { + state.status = 'failed'; + state.error = action.error; + }); + } }); export const { setGroupColumns } = groupSlice.actions; diff --git a/src/slices/healthSlice.ts b/src/slices/healthSlice.ts index 5d124dfbfa..89e84e3ec8 100644 --- a/src/slices/healthSlice.ts +++ b/src/slices/healthSlice.ts @@ -14,14 +14,14 @@ const MALFORMED_DATA = "Malformed Data"; const ERROR = "error"; export type HealthStatus = { - name: string, - error: boolean, - status: string, + name: string, + error: boolean, + status: string, } type HealthState = { - statusHealth: 'uninitialized' | 'loading' | 'succeeded' | 'failed', - errorHealth: SerializedError | null, + statusHealth: 'uninitialized' | 'loading' | 'succeeded' | 'failed', + errorHealth: SerializedError | null, service: HealthStatus[], error: boolean, numErr: number, @@ -29,125 +29,125 @@ type HealthState = { // Initial state of health status in redux store const initialState: HealthState = { - statusHealth: 'uninitialized', - errorHealth: null, - service: [ - { - name: STATES_NAMES, - error: false, - status: "", - }, - { - name: BACKEND_NAMES, - error: false, - status: "", - }, - ], - error: false, - numErr: 0, + statusHealth: 'uninitialized', + errorHealth: null, + service: [ + { + name: STATES_NAMES, + error: false, + status: "", + }, + { + name: BACKEND_NAMES, + error: false, + status: "", + }, + ], + error: false, + numErr: 0, }; type FetchHealthStatusResponse = { - health: { - healthy: number, - warning: number, - error: number, - } + health: { + healthy: number, + warning: number, + error: number, + } } // Fetch health status and transform it to further use export const fetchHealthStatus = createAppAsyncThunk('health/fetchHealthStatus', async () => { - const res = await axios.get("/services/health.json"); - return res.data; + const res = await axios.get("/services/health.json"); + return res.data; }); const healthSlice = createSlice({ - name: 'health', - initialState, - reducers: { - setError(state, action: PayloadAction<{ - error: HealthState["error"], - }>) { - state.error = action.payload.error; - }, + name: 'health', + initialState, + reducers: { + setError(state, action: PayloadAction<{ + error: HealthState["error"], + }>) { + state.error = action.payload.error; + }, addNumError(state, action: PayloadAction<{ - numError: HealthState["numErr"], - }>) { - state.numErr = state.numErr + action.payload.numError; - }, + numError: HealthState["numErr"], + }>) { + state.numErr = state.numErr + action.payload.numError; + }, resetNumError(state) { - state.numErr = 0; - }, - }, - // These are used for thunks - extraReducers: builder => { - builder - .addCase(fetchHealthStatus.pending, (state) => { - state.statusHealth = 'loading'; - }) - .addCase(fetchHealthStatus.fulfilled, (state, action: PayloadAction< - FetchHealthStatusResponse - >) => { - state.statusHealth = 'succeeded'; + state.numErr = 0; + }, + }, + // These are used for thunks + extraReducers: builder => { + builder + .addCase(fetchHealthStatus.pending, (state) => { + state.statusHealth = 'loading'; + }) + .addCase(fetchHealthStatus.fulfilled, (state, action: PayloadAction< + FetchHealthStatusResponse + >) => { + state.statusHealth = 'succeeded'; - const health = action.payload.health; - let healthStatus; - if (undefined === action.payload || undefined === health) { - healthStatus = { - name: STATES_NAMES, - status: MALFORMED_DATA, - error: true, - }; - state.service = mapHealthStatus(state, healthStatus); - state.error = true; - state.numErr += 1; - } else { - let abnormal = 0; - abnormal = health["warning"] + health["error"]; - if (abnormal === 0) { - healthStatus = { - name: BACKEND_NAMES, - status: OK, - error: false, - }; - state.service = mapHealthStatus(state, healthStatus); - state.error = false; - } else { - healthStatus = { - name: BACKEND_NAMES, - status: ERROR, - error: true, - }; - state.service = mapHealthStatus(state, healthStatus); - state.error = true; - } - state.numErr = abnormal; - } - }) - .addCase(fetchHealthStatus.rejected, (state, action) => { - state.statusHealth = 'failed'; + const health = action.payload.health; + let healthStatus; + if (undefined === action.payload || undefined === health) { + healthStatus = { + name: STATES_NAMES, + status: MALFORMED_DATA, + error: true, + }; + state.service = mapHealthStatus(state, healthStatus); + state.error = true; + state.numErr += 1; + } else { + let abnormal = 0; + abnormal = health["warning"] + health["error"]; + if (abnormal === 0) { + healthStatus = { + name: BACKEND_NAMES, + status: OK, + error: false, + }; + state.service = mapHealthStatus(state, healthStatus); + state.error = false; + } else { + healthStatus = { + name: BACKEND_NAMES, + status: ERROR, + error: true, + }; + state.service = mapHealthStatus(state, healthStatus); + state.error = true; + } + state.numErr = abnormal; + } + }) + .addCase(fetchHealthStatus.rejected, (state, action) => { + state.statusHealth = 'failed'; - let healthStatus = { - name: STATES_NAMES, - status: action.error.message ?? "", - error: true, - }; - state.service = mapHealthStatus(state, healthStatus); - state.error = true; - state.numErr += 1; + let healthStatus = { + name: STATES_NAMES, + status: action.error.message ?? "", + error: true, + }; + state.service = mapHealthStatus(state, healthStatus); + state.error = true; + state.numErr += 1; - state.errorHealth = action.error; - }); - } + state.errorHealth = action.error; + }); + } }); const mapHealthStatus = (state: WritableDraft, updatedHealthStatus: HealthStatus) => { - return state.service.map((healthStatus) => { - if (healthStatus.name === updatedHealthStatus.name) { - return updatedHealthStatus; - } - return healthStatus; - }); + return state.service.map((healthStatus) => { + if (healthStatus.name === updatedHealthStatus.name) { + return updatedHealthStatus; + } + return healthStatus; + }); } export const { setError } = healthSlice.actions; diff --git a/src/slices/jobSlice.ts b/src/slices/jobSlice.ts index 19e1aebc71..55937b1844 100644 --- a/src/slices/jobSlice.ts +++ b/src/slices/jobSlice.ts @@ -9,93 +9,93 @@ import { createAppAsyncThunk } from '../createAsyncThunkWithTypes'; * This file contains redux reducer for actions affecting the state of jobs */ export type Job = { - creator: string, - id: number, - operation: string, - processingHost: string, - processingNode: string, - started: string, - status: string, - submitted: string, - type: string, + creator: string, + id: number, + operation: string, + processingHost: string, + processingNode: string, + started: string, + status: string, + submitted: string, + type: string, } type JobState = { - status: 'uninitialized' | 'loading' | 'succeeded' | 'failed', - error: SerializedError | null, - results: Job[], - columns: TableConfig["columns"], - total: number, - count: number, - offset: number, - limit: number, + status: 'uninitialized' | 'loading' | 'succeeded' | 'failed', + error: SerializedError | null, + results: Job[], + columns: TableConfig["columns"], + total: number, + count: number, + offset: number, + limit: number, } // Fill columns initially with columns defined in jobsTableConfig const initialColumns = jobsTableConfig.columns.map((column) => ({ - ...column, - deactivated: false, + ...column, + deactivated: false, })); // Initial state of jobs in redux store const initialState: JobState = { - status: 'uninitialized', - error: null, - results: [], - columns: initialColumns, - total: 0, - count: 0, - offset: 0, - limit: 0, + status: 'uninitialized', + error: null, + results: [], + columns: initialColumns, + total: 0, + count: 0, + offset: 0, + limit: 0, }; export const fetchJobs = createAppAsyncThunk('jobs/fetchJobs', async (_, { getState }) => { - const state = getState(); - let params = getURLParams(state, "jobs"); - // Just make the async request here, and return the response. - // This will automatically dispatch a `pending` action first, - // and then `fulfilled` or `rejected` actions based on the promise. - // /jobs.json?limit=0&offset=0&filter={filter}&sort={sort} - const res = await axios.get("/admin-ng/job/jobs.json?", { params: params }); - return res.data; + const state = getState(); + let params = getURLParams(state, "jobs"); + // Just make the async request here, and return the response. + // This will automatically dispatch a `pending` action first, + // and then `fulfilled` or `rejected` actions based on the promise. + // /jobs.json?limit=0&offset=0&filter={filter}&sort={sort} + const res = await axios.get("/admin-ng/job/jobs.json?", { params: params }); + return res.data; }); const jobSlice = createSlice({ - name: 'jobs', - initialState, - reducers: { - setJobColumns(state, action: PayloadAction< - JobState["columns"] - >) { - state.columns = action.payload; - }, - }, - // These are used for thunks - extraReducers: builder => { - builder - .addCase(fetchJobs.pending, (state) => { - state.status = 'loading'; - }) - .addCase(fetchJobs.fulfilled, (state, action: PayloadAction<{ - total: JobState["total"], - count: JobState["count"], - limit: JobState["limit"], - offset: JobState["offset"], - results: JobState["results"], - }>) => { - state.status = 'succeeded'; - const jobs = action.payload; - state.total = jobs.total; - state.count = jobs.count; - state.limit = jobs.limit; - state.offset = jobs.offset; - state.results = jobs.results; - }) - .addCase(fetchJobs.rejected, (state, action) => { - state.status = 'failed'; - state.error = action.error; - }); - } + name: 'jobs', + initialState, + reducers: { + setJobColumns(state, action: PayloadAction< + JobState["columns"] + >) { + state.columns = action.payload; + }, + }, + // These are used for thunks + extraReducers: builder => { + builder + .addCase(fetchJobs.pending, (state) => { + state.status = 'loading'; + }) + .addCase(fetchJobs.fulfilled, (state, action: PayloadAction<{ + total: JobState["total"], + count: JobState["count"], + limit: JobState["limit"], + offset: JobState["offset"], + results: JobState["results"], + }>) => { + state.status = 'succeeded'; + const jobs = action.payload; + state.total = jobs.total; + state.count = jobs.count; + state.limit = jobs.limit; + state.offset = jobs.offset; + state.results = jobs.results; + }) + .addCase(fetchJobs.rejected, (state, action) => { + state.status = 'failed'; + state.error = action.error; + }); + } }); export const { setJobColumns } = jobSlice.actions; diff --git a/src/slices/notificationSlice.ts b/src/slices/notificationSlice.ts index 6de1e14f16..d6440ef33f 100644 --- a/src/slices/notificationSlice.ts +++ b/src/slices/notificationSlice.ts @@ -1,14 +1,14 @@ import { PayloadAction, createSlice } from '@reduxjs/toolkit' import { - NOTIFICATION_CONTEXT, - NOTIFICATION_CONTEXT_ACCESS, - NOTIFICATION_CONTEXT_TOBIRA, + NOTIFICATION_CONTEXT, + NOTIFICATION_CONTEXT_ACCESS, + NOTIFICATION_CONTEXT_TOBIRA, } from "../configs/modalConfig"; import { - ADMIN_NOTIFICATION_DURATION_ERROR, - ADMIN_NOTIFICATION_DURATION_GLOBAL, - ADMIN_NOTIFICATION_DURATION_SUCCESS, - ADMIN_NOTIFICATION_DURATION_WARNING, + ADMIN_NOTIFICATION_DURATION_ERROR, + ADMIN_NOTIFICATION_DURATION_GLOBAL, + ADMIN_NOTIFICATION_DURATION_SUCCESS, + ADMIN_NOTIFICATION_DURATION_WARNING, } from "../configs/generalConfig"; import { getLastAddedNotification } from '../selectors/notificationSelector'; import { createAppAsyncThunk } from '../createAsyncThunkWithTypes'; @@ -18,211 +18,211 @@ import { createAppAsyncThunk } from '../createAsyncThunkWithTypes'; */ // Calling this "OurNotification" because "Notification" is reserved by the Notifications Web API export type OurNotification = { - message: string, - id: number, - hidden: boolean, - duration: number, // in milliseconds. -1 means stay forever - type: "error" | "success" | "warning" | "info", - parameter?: { [key: string]: unknown }, - key: string, - context: string + message: string, + id: number, + hidden: boolean, + duration: number, // in milliseconds. -1 means stay forever + type: "error" | "success" | "warning" | "info", + parameter?: { [key: string]: unknown }, + key: string, + context: string } type NotificationState = { - notificationPositionGlobal: string, - notifications: OurNotification[] + notificationPositionGlobal: string, + notifications: OurNotification[] } // Initial state of notifications in store // If you want to change the position of notification, here it can be done const initialState: NotificationState = { - notificationPositionGlobal: "bottom-right", - notifications: [], + notificationPositionGlobal: "bottom-right", + notifications: [], }; // Counter for id of notifications let nextNotificationId = 0; export const addNotification = createAppAsyncThunk('notifications/addNotification', async (params: { - type: OurNotification["type"], - key: OurNotification["key"], - duration?: OurNotification["duration"], - parameter?: OurNotification["parameter"], - context?: OurNotification["context"], - id?: OurNotification["id"] - noDuplicates?: boolean, // Do not add this notification if one with the same key already exists (in the same context) + type: OurNotification["type"], + key: OurNotification["key"], + duration?: OurNotification["duration"], + parameter?: OurNotification["parameter"], + context?: OurNotification["context"], + id?: OurNotification["id"] + noDuplicates?: boolean, // Do not add this notification if one with the same key already exists (in the same context) }, {dispatch, getState}) => { - let { type, key, duration, parameter, context, id, noDuplicates } = params - - if (noDuplicates) { - const state = getState(); - for (const notif of state.notifications.notifications) { - if (notif.key === key && notif.context === context) { - console.log("Did not add notification with key " + key + " because a notification with that key already exists.") - return; - } - } - } - - if (!duration) { - // fall back to defaults - switch (type) { - case "error": - duration = ADMIN_NOTIFICATION_DURATION_ERROR; - break; - case "success": - duration = ADMIN_NOTIFICATION_DURATION_SUCCESS; - break; - case "warning": - duration = ADMIN_NOTIFICATION_DURATION_WARNING; - break; - default: - duration = ADMIN_NOTIFICATION_DURATION_GLOBAL; - break; - } - } - // default durations are in seconds. duration needs to be in milliseconds - if (duration > 0) duration *= 1000; - - if (!context) { - context = "global"; - } - - if (!parameter) { - parameter = {}; - } - - // Create new notification - const notification = { - id: 0, // value does not matter, id is set in action - type: type, - key: key, - message: "NOTIFICATIONS." + key, - parameter: parameter, - duration: duration, - hidden: false, - context: context, - }; - var dispatchedNotification; - if (!id) { - dispatchedNotification = dispatch(createNotification({notification: notification, id: nextNotificationId++})); - } else { - dispatchedNotification = dispatch(createNotification({notification: notification, id: id})); - } - - // Get newly created notification and its id - let latestNotification = getLastAddedNotification(getState()); - - // Fade out notification if it is not -1 -> -1 means 'stay forever' - // Start timeout for fading out after time in duration is over - if ( - latestNotification.duration && - latestNotification.duration !== -1 - ) { - setTimeout( - () => dispatch(removeNotification(latestNotification.id)), - latestNotification.duration - ); - } - - return dispatchedNotification.payload.id; + let { type, key, duration, parameter, context, id, noDuplicates } = params + + if (noDuplicates) { + const state = getState(); + for (const notif of state.notifications.notifications) { + if (notif.key === key && notif.context === context) { + console.log("Did not add notification with key " + key + " because a notification with that key already exists.") + return; + } + } + } + + if (!duration) { + // fall back to defaults + switch (type) { + case "error": + duration = ADMIN_NOTIFICATION_DURATION_ERROR; + break; + case "success": + duration = ADMIN_NOTIFICATION_DURATION_SUCCESS; + break; + case "warning": + duration = ADMIN_NOTIFICATION_DURATION_WARNING; + break; + default: + duration = ADMIN_NOTIFICATION_DURATION_GLOBAL; + break; + } + } + // default durations are in seconds. duration needs to be in milliseconds + if (duration > 0) duration *= 1000; + + if (!context) { + context = "global"; + } + + if (!parameter) { + parameter = {}; + } + + // Create new notification + const notification = { + id: 0, // value does not matter, id is set in action + type: type, + key: key, + message: "NOTIFICATIONS." + key, + parameter: parameter, + duration: duration, + hidden: false, + context: context, + }; + var dispatchedNotification; + if (!id) { + dispatchedNotification = dispatch(createNotification({notification: notification, id: nextNotificationId++})); + } else { + dispatchedNotification = dispatch(createNotification({notification: notification, id: id})); + } + + // Get newly created notification and its id + let latestNotification = getLastAddedNotification(getState()); + + // Fade out notification if it is not -1 -> -1 means 'stay forever' + // Start timeout for fading out after time in duration is over + if ( + latestNotification.duration && + latestNotification.duration !== -1 + ) { + setTimeout( + () => dispatch(removeNotification(latestNotification.id)), + latestNotification.duration + ); + } + + return dispatchedNotification.payload.id; }); // Reducer for notifications const notificationSlice = createSlice({ - name: 'notifications', - initialState, - reducers: { - createNotification(state, action: PayloadAction<{ - notification: OurNotification, - id: OurNotification["id"], - }>) { - const { notification, id } = action.payload; - if (state.notifications.filter(e => e.id === id).length > 0) { - console.log("Notification with id: " + id + " already exists.") - state.notifications = state.notifications.map((oldNotification) => { - if (oldNotification.id === id) { - return { - ...notification, - id: id, - }; - } - return oldNotification; - }) - } else { - state.notifications = [ - ...state.notifications, - { - id: id, - key: notification.key, - message: notification.message, - type: notification.type, - hidden: notification.hidden, - duration: notification.duration, - parameter: notification.parameter, - context: notification.context, - }, - ] - } - }, - removeNotification(state, action: PayloadAction< - OurNotification["id"] - >) { - const idToRemove = action.payload; - state.notifications = state.notifications.filter( - (notification) => notification.id !== idToRemove - ) - }, - removeNotificationByKey(state, action: PayloadAction<{ - key: OurNotification["key"], - context: OurNotification["context"], - }>) { - const { key, context } = action.payload; - state.notifications = state.notifications.filter( - (notification) => notification.key !== key || notification.context !== context - ) - }, - removeNotificationWizardForm(state) { - state.notifications = state.notifications.filter( - (notification) => notification.context !== NOTIFICATION_CONTEXT - ) - }, - removeNotificationWizardAccess(state) { - state.notifications = state.notifications.filter( - (notification) => notification.context !== NOTIFICATION_CONTEXT_ACCESS - ) - }, - removeNotificationWizardTobira(state) { - state.notifications = state.notifications.filter( - (notification) => notification.context !== NOTIFICATION_CONTEXT_TOBIRA - ) - }, - setHidden(state, action: PayloadAction<{ - id: OurNotification["id"], - isHidden: OurNotification["hidden"], - }>) { - const { id: idToUpdate, isHidden } = action.payload; - state.notifications = state.notifications.map((notification) => { - if (notification.id === idToUpdate) { - return { - ...notification, - hidden: isHidden, - }; - } - return notification; - }) - }, - }, + name: 'notifications', + initialState, + reducers: { + createNotification(state, action: PayloadAction<{ + notification: OurNotification, + id: OurNotification["id"], + }>) { + const { notification, id } = action.payload; + if (state.notifications.filter(e => e.id === id).length > 0) { + console.log("Notification with id: " + id + " already exists.") + state.notifications = state.notifications.map((oldNotification) => { + if (oldNotification.id === id) { + return { + ...notification, + id: id, + }; + } + return oldNotification; + }) + } else { + state.notifications = [ + ...state.notifications, + { + id: id, + key: notification.key, + message: notification.message, + type: notification.type, + hidden: notification.hidden, + duration: notification.duration, + parameter: notification.parameter, + context: notification.context, + }, + ] + } + }, + removeNotification(state, action: PayloadAction< + OurNotification["id"] + >) { + const idToRemove = action.payload; + state.notifications = state.notifications.filter( + (notification) => notification.id !== idToRemove + ) + }, + removeNotificationByKey(state, action: PayloadAction<{ + key: OurNotification["key"], + context: OurNotification["context"], + }>) { + const { key, context } = action.payload; + state.notifications = state.notifications.filter( + (notification) => notification.key !== key || notification.context !== context + ) + }, + removeNotificationWizardForm(state) { + state.notifications = state.notifications.filter( + (notification) => notification.context !== NOTIFICATION_CONTEXT + ) + }, + removeNotificationWizardAccess(state) { + state.notifications = state.notifications.filter( + (notification) => notification.context !== NOTIFICATION_CONTEXT_ACCESS + ) + }, + removeNotificationWizardTobira(state) { + state.notifications = state.notifications.filter( + (notification) => notification.context !== NOTIFICATION_CONTEXT_TOBIRA + ) + }, + setHidden(state, action: PayloadAction<{ + id: OurNotification["id"], + isHidden: OurNotification["hidden"], + }>) { + const { id: idToUpdate, isHidden } = action.payload; + state.notifications = state.notifications.map((notification) => { + if (notification.id === idToUpdate) { + return { + ...notification, + hidden: isHidden, + }; + } + return notification; + }) + }, + }, }); export const { - createNotification, - removeNotification, - removeNotificationByKey, - removeNotificationWizardForm, - removeNotificationWizardAccess, - removeNotificationWizardTobira, - setHidden, + createNotification, + removeNotification, + removeNotificationByKey, + removeNotificationWizardForm, + removeNotificationWizardAccess, + removeNotificationWizardTobira, + setHidden, } = notificationSlice.actions; // Export the slice reducer as the default export diff --git a/src/slices/recordingDetailsSlice.ts b/src/slices/recordingDetailsSlice.ts index 40ef5959f7..ddda1e0be8 100644 --- a/src/slices/recordingDetailsSlice.ts +++ b/src/slices/recordingDetailsSlice.ts @@ -6,76 +6,76 @@ import { createAppAsyncThunk } from '../createAsyncThunkWithTypes'; * This file contains redux reducer for actions affecting the state of a recording/capture agent */ export interface RecordingDetails { - name: string, - status: string, - update: string, - url: string, - capabilities: { key: string, value: string }[], - configuration: { key: string, value: string }[], - inputs: { id: string, value: string }[], + name: string, + status: string, + update: string, + url: string, + capabilities: { key: string, value: string }[], + configuration: { key: string, value: string }[], + inputs: { id: string, value: string }[], } interface RecordingDetailsState extends RecordingDetails { - statusRecordingDetails: 'uninitialized' | 'loading' | 'succeeded' | 'failed', - errorRecordingDetails: SerializedError | null, + statusRecordingDetails: 'uninitialized' | 'loading' | 'succeeded' | 'failed', + errorRecordingDetails: SerializedError | null, } // Initial state of recording details in redux store const initialState: RecordingDetailsState = { - statusRecordingDetails: 'uninitialized', - errorRecordingDetails: null, - name: "", - status: "", - update: "", - url: "", - capabilities: [], - configuration: [], - inputs: [], + statusRecordingDetails: 'uninitialized', + errorRecordingDetails: null, + name: "", + status: "", + update: "", + url: "", + capabilities: [], + configuration: [], + inputs: [], }; // fetch details of certain recording from server export const fetchRecordingDetails = createAppAsyncThunk('recordingDetails/fetchRecordingDetails', async (name: RecordingDetails["name"]) => { - // Just make the async request here, and return the response. - // This will automatically dispatch a `pending` action first, - // and then `fulfilled` or `rejected` actions based on the promise. - const res = await axios.get(`/admin-ng/capture-agents/${name}`); - return res.data; + // Just make the async request here, and return the response. + // This will automatically dispatch a `pending` action first, + // and then `fulfilled` or `rejected` actions based on the promise. + const res = await axios.get(`/admin-ng/capture-agents/${name}`); + return res.data; }); const recordingDetailsSlice = createSlice({ - name: 'recordingDetails', - initialState, - reducers: {}, - // These are used for thunks - extraReducers: builder => { - builder - .addCase(fetchRecordingDetails.pending, (state) => { - state.statusRecordingDetails = 'loading'; - }) - .addCase(fetchRecordingDetails.fulfilled, (state, action: PayloadAction<{ - Name: RecordingDetailsState["name"], - Status: RecordingDetailsState["status"], - Update: RecordingDetailsState["update"], - URL: RecordingDetailsState["url"], - capabilities: RecordingDetailsState["capabilities"], - configuration: RecordingDetailsState["configuration"], - inputs: RecordingDetailsState["inputs"], - }>) => { - state.statusRecordingDetails = 'succeeded'; - const recordingDetails = action.payload; - state.name = recordingDetails.Name; - state.status = recordingDetails.Status; - state.update = recordingDetails.Update; - state.url = recordingDetails.URL; - state.capabilities = recordingDetails.capabilities; - state.configuration = recordingDetails.configuration; - state.inputs = recordingDetails.inputs; - }) - .addCase(fetchRecordingDetails.rejected, (state, action) => { - state.statusRecordingDetails = 'failed'; - state.errorRecordingDetails = action.error; - }); - } + name: 'recordingDetails', + initialState, + reducers: {}, + // These are used for thunks + extraReducers: builder => { + builder + .addCase(fetchRecordingDetails.pending, (state) => { + state.statusRecordingDetails = 'loading'; + }) + .addCase(fetchRecordingDetails.fulfilled, (state, action: PayloadAction<{ + Name: RecordingDetailsState["name"], + Status: RecordingDetailsState["status"], + Update: RecordingDetailsState["update"], + URL: RecordingDetailsState["url"], + capabilities: RecordingDetailsState["capabilities"], + configuration: RecordingDetailsState["configuration"], + inputs: RecordingDetailsState["inputs"], + }>) => { + state.statusRecordingDetails = 'succeeded'; + const recordingDetails = action.payload; + state.name = recordingDetails.Name; + state.status = recordingDetails.Status; + state.update = recordingDetails.Update; + state.url = recordingDetails.URL; + state.capabilities = recordingDetails.capabilities; + state.configuration = recordingDetails.configuration; + state.inputs = recordingDetails.inputs; + }) + .addCase(fetchRecordingDetails.rejected, (state, action) => { + state.statusRecordingDetails = 'failed'; + state.errorRecordingDetails = action.error; + }); + } }); // export const {} = recordingDetailsSlice.actions; diff --git a/src/slices/recordingSlice.ts b/src/slices/recordingSlice.ts index 153ae00f23..de961b1856 100644 --- a/src/slices/recordingSlice.ts +++ b/src/slices/recordingSlice.ts @@ -10,148 +10,148 @@ import { createAppAsyncThunk } from '../createAsyncThunkWithTypes'; * This file contains redux reducer for actions affecting the state of recordings */ export type Recording = { - id: string, - inputs: { id: string, value: string }[], - name: string, - removable: boolean, - roomId: string, - status: string, - type: string, - updated: string, - url: string, + id: string, + inputs: { id: string, value: string }[], + name: string, + removable: boolean, + roomId: string, + status: string, + type: string, + updated: string, + url: string, } type RecordingState = { - status: 'uninitialized' | 'loading' | 'succeeded' | 'failed', - error: SerializedError | null, - results: Recording[], - columns: TableConfig["columns"], - total: number, - count: number, - offset: number, - limit: number, + status: 'uninitialized' | 'loading' | 'succeeded' | 'failed', + error: SerializedError | null, + results: Recording[], + columns: TableConfig["columns"], + total: number, + count: number, + offset: number, + limit: number, } // Fill columns initially with columns defined in recordingsTableConfig const initialColumns = recordingsTableConfig.columns.map((column) => ({ - ...column, - deactivated: false, + ...column, + deactivated: false, })); // Initial state of recordings in redux store const initialState: RecordingState = { - status: 'uninitialized', - error: null, - results: [], - columns: initialColumns, - total: 0, - count: 0, - offset: 0, - limit: 0, + status: 'uninitialized', + error: null, + results: [], + columns: initialColumns, + total: 0, + count: 0, + offset: 0, + limit: 0, }; // fetch recordings from server export const fetchRecordings = createAppAsyncThunk('recordings/fetchRecordings', async (flag: string | undefined, { getState }) => { - let res; - - if (flag === "inputs") { - res = await axios.get( - "/admin-ng/capture-agents/agents.json?inputs=true" - ); - } else { - const state = getState(); - let params = getURLParams(state, "recordings"); - - // /agents.json?filter={filter}&limit=100&offset=0&inputs=false&sort={sort} - res = await axios.get("/admin-ng/capture-agents/agents.json", { - params: params, - }); - } - - const recordings = await res.data; - - let captureAgents = []; - - for (const agent of recordings.results) { - const transformedAgent = { - id: agent.Name, - name: agent.Name, - status: agent.Status, - updated: agent.Update, - inputs: !!agent.inputs ? [...agent.inputs] : [], - roomId: !!agent.roomId ? agent.roomId : "", - type: "LOCATION", - url: !!agent.url ? agent.url : "", - removable: - "AGENTS.STATUS.OFFLINE" === agent.Status || - "AGENTS.STATUS.UNKNOWN" === agent.Status, - }; - - captureAgents.push(transformedAgent); - } - - return { ...recordings, results: captureAgents } + let res; + + if (flag === "inputs") { + res = await axios.get( + "/admin-ng/capture-agents/agents.json?inputs=true" + ); + } else { + const state = getState(); + let params = getURLParams(state, "recordings"); + + // /agents.json?filter={filter}&limit=100&offset=0&inputs=false&sort={sort} + res = await axios.get("/admin-ng/capture-agents/agents.json", { + params: params, + }); + } + + const recordings = await res.data; + + let captureAgents = []; + + for (const agent of recordings.results) { + const transformedAgent = { + id: agent.Name, + name: agent.Name, + status: agent.Status, + updated: agent.Update, + inputs: !!agent.inputs ? [...agent.inputs] : [], + roomId: !!agent.roomId ? agent.roomId : "", + type: "LOCATION", + url: !!agent.url ? agent.url : "", + removable: + "AGENTS.STATUS.OFFLINE" === agent.Status || + "AGENTS.STATUS.UNKNOWN" === agent.Status, + }; + + captureAgents.push(transformedAgent); + } + + return { ...recordings, results: captureAgents } }); // delete location with provided id export const deleteRecording = createAppAsyncThunk('recordings/deleteRecording', async (id: Recording["id"], { dispatch }) => { - // API call for deleting a location - axios - .delete(`/admin-ng/capture-agents/${id}`) - .then((res) => { - console.info(res); - // add success notification - dispatch(addNotification({type: "success", key: "LOCATION_DELETED"})); - }) - .catch((res) => { - console.error(res); - // add error notification depending on status code - if (res.status === 401) { - dispatch( - addNotification({type: "error", key: "LOCATION_NOT_DELETED_NOT_AUTHORIZED"}) - ); - } else { - dispatch(addNotification({type: "error", key: "LOCATION_NOT_DELETED"})); - } - }); + // API call for deleting a location + axios + .delete(`/admin-ng/capture-agents/${id}`) + .then((res) => { + console.info(res); + // add success notification + dispatch(addNotification({type: "success", key: "LOCATION_DELETED"})); + }) + .catch((res) => { + console.error(res); + // add error notification depending on status code + if (res.status === 401) { + dispatch( + addNotification({type: "error", key: "LOCATION_NOT_DELETED_NOT_AUTHORIZED"}) + ); + } else { + dispatch(addNotification({type: "error", key: "LOCATION_NOT_DELETED"})); + } + }); }); const recordingSlice = createSlice({ - name: 'recordings', - initialState, - reducers: { - setRecordingsColumns(state, action: PayloadAction< - RecordingState["columns"] - >) { - state.columns = action.payload; - }, - }, - // These are used for thunks - extraReducers: builder => { - builder - .addCase(fetchRecordings.pending, (state) => { - state.status = 'loading'; - }) - .addCase(fetchRecordings.fulfilled, (state, action: PayloadAction<{ - total: RecordingState["total"], - count: RecordingState["count"], - limit: RecordingState["limit"], - offset: RecordingState["offset"], - results: RecordingState["results"], - }>) => { - state.status = 'succeeded'; - const recordings = action.payload; - state.total = recordings.total; - state.count = recordings.count; - state.limit = recordings.limit; - state.offset = recordings.offset; - state.results = recordings.results; - }) - .addCase(fetchRecordings.rejected, (state, action) => { - state.status = 'failed'; - state.error = action.error; - }); - } + name: 'recordings', + initialState, + reducers: { + setRecordingsColumns(state, action: PayloadAction< + RecordingState["columns"] + >) { + state.columns = action.payload; + }, + }, + // These are used for thunks + extraReducers: builder => { + builder + .addCase(fetchRecordings.pending, (state) => { + state.status = 'loading'; + }) + .addCase(fetchRecordings.fulfilled, (state, action: PayloadAction<{ + total: RecordingState["total"], + count: RecordingState["count"], + limit: RecordingState["limit"], + offset: RecordingState["offset"], + results: RecordingState["results"], + }>) => { + state.status = 'succeeded'; + const recordings = action.payload; + state.total = recordings.total; + state.count = recordings.count; + state.limit = recordings.limit; + state.offset = recordings.offset; + state.results = recordings.results; + }) + .addCase(fetchRecordings.rejected, (state, action) => { + state.status = 'failed'; + state.error = action.error; + }); + } }); export const { setRecordingsColumns } = recordingSlice.actions; diff --git a/src/slices/seriesSlice.ts b/src/slices/seriesSlice.ts index 90442c397c..15207c191e 100644 --- a/src/slices/seriesSlice.ts +++ b/src/slices/seriesSlice.ts @@ -2,13 +2,13 @@ import { PayloadAction, SerializedError, createSlice } from '@reduxjs/toolkit' import { seriesTableConfig } from '../configs/tableConfigs/seriesTableConfig'; import axios from 'axios'; import { - getURLParams, - prepareAccessPolicyRulesForPost, - prepareMetadataFieldsForPost, - transformMetadataCollection, + getURLParams, + prepareAccessPolicyRulesForPost, + prepareMetadataFieldsForPost, + transformMetadataCollection, } from "../utils/resourceUtils"; import { - transformToIdValueArray, + transformToIdValueArray, } from "../utils/utils"; import { addNotification } from './notificationSlice'; import { TableConfig } from '../configs/tableConfigs/aclsTableConfig'; @@ -21,483 +21,483 @@ import { handleTobiraError } from './shared/tobiraErrors'; * This file contains redux reducer for actions affecting the state of series */ export type Series = { - contributors: string[], - createdBy?: string, - creation_date?: string, - id: string, - language?: string, - license?: string, - managedAcl?: string, - organizers: string[], - rightsHolder?: string, - title: string, + contributors: string[], + createdBy?: string, + creation_date?: string, + id: string, + language?: string, + license?: string, + managedAcl?: string, + organizers: string[], + rightsHolder?: string, + title: string, } type Theme = { - description: string, - id: string, - name: string, + description: string, + id: string, + name: string, } export interface TobiraPage { - title?: string, - path: string, - segment: string, - children: TobiraPage[], - ancestors: TobiraPage[] - - subpages?: string, // not returned by endpoint - new?: boolean, // not returned by endpoint - blocks: { - id: string, - }[], + title?: string, + path: string, + segment: string, + children: TobiraPage[], + ancestors: TobiraPage[] + + subpages?: string, // not returned by endpoint + new?: boolean, // not returned by endpoint + blocks: { + id: string, + }[], } type SeriesState = { - status: 'uninitialized' | 'loading' | 'succeeded' | 'failed', - error: SerializedError | null, - statusMetadata: 'uninitialized' | 'loading' | 'succeeded' | 'failed', - errorMetadata: SerializedError | null, - statusThemes: 'uninitialized' | 'loading' | 'succeeded' | 'failed', - errorThemes: SerializedError | null, - statusTobiraPage: 'uninitialized' | 'loading' | 'succeeded' | 'failed', - errorTobiraPage: SerializedError | null, - results: Series[], - columns: TableConfig["columns"], - showActions: boolean, - total: number, - count: number, - offset: number, - limit: number, - metadata: MetadataCatalog, - extendedMetadata: MetadataCatalog[], - themes: Theme[], - deletionAllowed: boolean, - hasEvents: boolean, - tobiraPage: TobiraPage, + status: 'uninitialized' | 'loading' | 'succeeded' | 'failed', + error: SerializedError | null, + statusMetadata: 'uninitialized' | 'loading' | 'succeeded' | 'failed', + errorMetadata: SerializedError | null, + statusThemes: 'uninitialized' | 'loading' | 'succeeded' | 'failed', + errorThemes: SerializedError | null, + statusTobiraPage: 'uninitialized' | 'loading' | 'succeeded' | 'failed', + errorTobiraPage: SerializedError | null, + results: Series[], + columns: TableConfig["columns"], + showActions: boolean, + total: number, + count: number, + offset: number, + limit: number, + metadata: MetadataCatalog, + extendedMetadata: MetadataCatalog[], + themes: Theme[], + deletionAllowed: boolean, + hasEvents: boolean, + tobiraPage: TobiraPage, } // Fill columns initially with columns defined in seriesTableConfig const initialColumns = seriesTableConfig.columns.map((column) => ({ - ...column, - deactivated: false, + ...column, + deactivated: false, })); // Initial state of series in redux store const initialState: SeriesState = { - status: 'uninitialized', - error: null, - statusMetadata: 'uninitialized', - errorMetadata: null, - statusThemes: 'uninitialized', - errorThemes: null, - statusTobiraPage: 'uninitialized', - errorTobiraPage: null, - results: [], - columns: initialColumns, - showActions: false, - total: 0, - count: 0, - offset: 0, - limit: 0, - metadata: { - title: "", - flavor: "", - fields: [], - }, - extendedMetadata: [], - themes: [], - deletionAllowed: true, - hasEvents: false, - tobiraPage: { - title: undefined, - path: "/", - segment: "", - children: [], - ancestors: [], - blocks: [], - }, + status: 'uninitialized', + error: null, + statusMetadata: 'uninitialized', + errorMetadata: null, + statusThemes: 'uninitialized', + errorThemes: null, + statusTobiraPage: 'uninitialized', + errorTobiraPage: null, + results: [], + columns: initialColumns, + showActions: false, + total: 0, + count: 0, + offset: 0, + limit: 0, + metadata: { + title: "", + flavor: "", + fields: [], + }, + extendedMetadata: [], + themes: [], + deletionAllowed: true, + hasEvents: false, + tobiraPage: { + title: undefined, + path: "/", + segment: "", + children: [], + ancestors: [], + blocks: [], + }, }; // fetch series from server export const fetchSeries = createAppAsyncThunk('series/fetchSeries', async (_, { getState }) => { - const state = getState(); - let params = getURLParams(state, "series"); - // Just make the async request here, and return the response. - // This will automatically dispatch a `pending` action first, - // and then `fulfilled` or `rejected` actions based on the promise. - // /series.json?sortorganizer={sortorganizer}&sort={sort}&filter={filter}&offset=0&limit=100 - const res = await axios.get("/admin-ng/series/series.json", { params: params }); - return res.data; + const state = getState(); + let params = getURLParams(state, "series"); + // Just make the async request here, and return the response. + // This will automatically dispatch a `pending` action first, + // and then `fulfilled` or `rejected` actions based on the promise. + // /series.json?sortorganizer={sortorganizer}&sort={sort}&filter={filter}&offset=0&limit=100 + const res = await axios.get("/admin-ng/series/series.json", { params: params }); + return res.data; }); // fetch series metadata from server export const fetchSeriesMetadata = createAppAsyncThunk('series/fetchSeriesMetadata', async (_, { rejectWithValue }) => { - const res = await axios.get("/admin-ng/series/new/metadata"); - const data = await res.data; - - const mainCatalog = "dublincore/series"; - let metadata: SeriesState["metadata"] | undefined = undefined; - const extendedMetadata = []; - - for (const metadataCatalog of data) { - if (metadataCatalog.flavor === mainCatalog) { - metadata = transformMetadataCollection({ ...metadataCatalog }); - } else { - extendedMetadata.push( - transformMetadataCollection({ ...metadataCatalog }) - ); - } - } - - if (!metadata) { - console.error("Main metadata catalog is missing"); - return rejectWithValue("Main metadata catalog is missing") - } - - return { metadata, extendedMetadata } + const res = await axios.get("/admin-ng/series/new/metadata"); + const data = await res.data; + + const mainCatalog = "dublincore/series"; + let metadata: SeriesState["metadata"] | undefined = undefined; + const extendedMetadata = []; + + for (const metadataCatalog of data) { + if (metadataCatalog.flavor === mainCatalog) { + metadata = transformMetadataCollection({ ...metadataCatalog }); + } else { + extendedMetadata.push( + transformMetadataCollection({ ...metadataCatalog }) + ); + } + } + + if (!metadata) { + console.error("Main metadata catalog is missing"); + return rejectWithValue("Main metadata catalog is missing") + } + + return { metadata, extendedMetadata } }); // fetch series themes from server export const fetchSeriesThemes = createAppAsyncThunk('series/fetchSeriesThemes', async () => { - let res = await axios.get("/admin-ng/series/new/themes"); - const data = await res.data as { [key: string]: { name: string, description: string } }; - // Transform object of objects to array of objects - const themes = Object.keys(data).map((key) => { - return { - id: key, - ...data[key], - }; - }); - return themes; + let res = await axios.get("/admin-ng/series/new/themes"); + const data = await res.data as { [key: string]: { name: string, description: string } }; + // Transform object of objects to array of objects + const themes = Object.keys(data).map((key) => { + return { + id: key, + ...data[key], + }; + }); + return themes; }); // post new series to backend export const postNewSeries = createAppAsyncThunk('series/postNewSeries', async (params: { - values: { - [key: string]: any; - acls: TransformedAcl[], - // contributor: string[], - // creator: string[], - // description: string, - // language: string, - // license: string, - // publisher: string[], - // rightsHolder: string, - // subject: string, - theme: string, - // title: string, - selectedPage?: TobiraPage, - breadcrumbs?: TobiraPage[], - }, - metadataInfo: MetadataCatalog, - extendedMetadata: MetadataCatalog[] + values: { + [key: string]: any; + acls: TransformedAcl[], + // contributor: string[], + // creator: string[], + // description: string, + // language: string, + // license: string, + // publisher: string[], + // rightsHolder: string, + // subject: string, + theme: string, + // title: string, + selectedPage?: TobiraPage, + breadcrumbs?: TobiraPage[], + }, + metadataInfo: MetadataCatalog, + extendedMetadata: MetadataCatalog[] }, {dispatch}) => { - const { values, metadataInfo, extendedMetadata } = params - - // prepare metadata provided by user - const metadata = prepareMetadataFieldsForPost( - [metadataInfo], - values - ); - const extendedMetadataCatalogs = prepareMetadataFieldsForPost( - extendedMetadata, - values - ); - - // metadata for post request - for (const entry of extendedMetadataCatalogs) { - metadata.push(entry); - } - - const access = prepareAccessPolicyRulesForPost(values.acls); - - // Tobira - let tobira: any = {}; - if (values.selectedPage && values.breadcrumbs) { - let existingPages: any[] = []; - let newPages: any[] = []; - values.breadcrumbs.concat(values.selectedPage).forEach( function (page: TobiraPage) { - if (page.new) { - newPages.push({ - name: page.title, - pathSegment: page.segment, - }); - } else { - existingPages.push(page); - } - }); - - tobira["parentPagePath"] = existingPages.pop().path; - tobira["newPages"] = newPages; - } - - - let jsonData: { - metadata: typeof metadata, - options: {}, - access: typeof access, - theme?: number, - tobira?: any - } = { - metadata: metadata, - options: {}, - access: access, - tobira: tobira, - }; - - if (values.theme !== "") { - jsonData = { - ...jsonData, - theme: parseInt(values.theme), - }; - } - - let data = new URLSearchParams(); - data.append("metadata", JSON.stringify(jsonData)); - - // Todo: process bar notification - axios - .post("/admin-ng/series/new", data.toString(), { - headers: { - "Content-Type": "application/x-www-form-urlencoded", - }, - }) - .then((response) => { - console.info(response); - dispatch(addNotification({type: "success", key: "SERIES_ADDED"})); - }) - .catch((response) => { - console.error(response); - dispatch(addNotification({type: "error", key: "SERIES_NOT_SAVED"})); - }); + const { values, metadataInfo, extendedMetadata } = params + + // prepare metadata provided by user + const metadata = prepareMetadataFieldsForPost( + [metadataInfo], + values + ); + const extendedMetadataCatalogs = prepareMetadataFieldsForPost( + extendedMetadata, + values + ); + + // metadata for post request + for (const entry of extendedMetadataCatalogs) { + metadata.push(entry); + } + + const access = prepareAccessPolicyRulesForPost(values.acls); + + // Tobira + let tobira: any = {}; + if (values.selectedPage && values.breadcrumbs) { + let existingPages: any[] = []; + let newPages: any[] = []; + values.breadcrumbs.concat(values.selectedPage).forEach( function (page: TobiraPage) { + if (page.new) { + newPages.push({ + name: page.title, + pathSegment: page.segment, + }); + } else { + existingPages.push(page); + } + }); + + tobira["parentPagePath"] = existingPages.pop().path; + tobira["newPages"] = newPages; + } + + + let jsonData: { + metadata: typeof metadata, + options: {}, + access: typeof access, + theme?: number, + tobira?: any + } = { + metadata: metadata, + options: {}, + access: access, + tobira: tobira, + }; + + if (values.theme !== "") { + jsonData = { + ...jsonData, + theme: parseInt(values.theme), + }; + } + + let data = new URLSearchParams(); + data.append("metadata", JSON.stringify(jsonData)); + + // Todo: process bar notification + axios + .post("/admin-ng/series/new", data.toString(), { + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + }) + .then((response) => { + console.info(response); + dispatch(addNotification({type: "success", key: "SERIES_ADDED"})); + }) + .catch((response) => { + console.error(response); + dispatch(addNotification({type: "error", key: "SERIES_NOT_SAVED"})); + }); }); // check for events of the series and if deleting the series if it has events is allowed export const checkForEventsDeleteSeriesModal = createAppAsyncThunk('series/checkForEventsDeleteSeriesModal', async (id: Series["id"], {dispatch}) => { - const hasEventsRequest = await axios.get( - `/admin-ng/series/${id}/hasEvents.json` - ); - const hasEventsResponse = await hasEventsRequest.data; - const hasEvents = hasEventsResponse.hasEvents; - - const deleteWithEventsAllowedRequest = await axios.get( - "/admin-ng/series/configuration.json" - ); - const deleteWithEventsAllowedResponse = await deleteWithEventsAllowedRequest.data; - const deleteWithEventsAllowed = - deleteWithEventsAllowedResponse.deleteSeriesWithEventsAllowed; - - dispatch( - setSeriesDeletionAllowed({ deletionAllowed: !hasEvents || deleteWithEventsAllowed, hasEvents: hasEvents }) - ); + const hasEventsRequest = await axios.get( + `/admin-ng/series/${id}/hasEvents.json` + ); + const hasEventsResponse = await hasEventsRequest.data; + const hasEvents = hasEventsResponse.hasEvents; + + const deleteWithEventsAllowedRequest = await axios.get( + "/admin-ng/series/configuration.json" + ); + const deleteWithEventsAllowedResponse = await deleteWithEventsAllowedRequest.data; + const deleteWithEventsAllowed = + deleteWithEventsAllowedResponse.deleteSeriesWithEventsAllowed; + + dispatch( + setSeriesDeletionAllowed({ deletionAllowed: !hasEvents || deleteWithEventsAllowed, hasEvents: hasEvents }) + ); }); // delete series with provided id export const deleteSeries = createAppAsyncThunk('series/deleteSeries', async (id: Series["id"], {dispatch}) => { - // API call for deleting a series - axios - .delete(`/admin-ng/series/${id}`) - .then((res) => { - console.info(res); - // add success notification - dispatch(addNotification({type: "success", key: "SERIES_DELETED"})); - }) - .catch((res) => { - console.error(res); - // add error notification - dispatch(addNotification({type: "error", key: "SERIES_NOT_DELETED"})); - }); + // API call for deleting a series + axios + .delete(`/admin-ng/series/${id}`) + .then((res) => { + console.info(res); + // add success notification + dispatch(addNotification({type: "success", key: "SERIES_DELETED"})); + }) + .catch((res) => { + console.error(res); + // add error notification + dispatch(addNotification({type: "error", key: "SERIES_NOT_DELETED"})); + }); }); // delete series with provided ids export const deleteMultipleSeries = createAppAsyncThunk('series/deleteMultipleSeries', async ( - series: { - contributors: string[], - createdBy: string, - creation_date: string, - hasEvents: false, - id: string, - organizers: string[], - selected: boolean, - title: string, - }[], + series: { + contributors: string[], + createdBy: string, + creation_date: string, + hasEvents: false, + id: string, + organizers: string[], + selected: boolean, + title: string, + }[], {dispatch}) => { - let data = []; - - for (let i = 0; i < series.length; i++) { - if (series[i].selected) { - data.push(series[i].id); - } - } - - axios - .post("/admin-ng/series/deleteSeries", data) - .then((res) => { - console.info(res); - //add success notification - dispatch(addNotification({type: "success", key: "SERIES_DELETED"})); - }) - .catch((res) => { - console.error(res); - //add error notification - dispatch(addNotification({type: "error", key: "SERIES_NOT_DELETED"})); - }); + let data = []; + + for (let i = 0; i < series.length; i++) { + if (series[i].selected) { + data.push(series[i].id); + } + } + + axios + .post("/admin-ng/series/deleteSeries", data) + .then((res) => { + console.info(res); + //add success notification + dispatch(addNotification({type: "success", key: "SERIES_DELETED"})); + }) + .catch((res) => { + console.error(res); + //add error notification + dispatch(addNotification({type: "error", key: "SERIES_NOT_DELETED"})); + }); }); // fetch metadata of certain series from server export const fetchSeriesDetailsTobiraNew = createAppAsyncThunk('seriesDetails/fetchSeriesDetailsTobiraNew', async (path: TobiraPage["path"], {dispatch}) => { - const res = await axios.get(`/admin-ng/series/new/tobira/page?path=` + path) - .catch(response => handleTobiraError(response, dispatch)); + const res = await axios.get(`/admin-ng/series/new/tobira/page?path=` + path) + .catch(response => handleTobiraError(response, dispatch)); - if (!res) { - throw Error; - } + if (!res) { + throw Error; + } - const data = res.data; - return data; + const data = res.data; + return data; }); // Get names and ids of selectable series export const fetchSeriesOptions = async () => { - let data = await axios.get("/admin-ng/resources/SERIES.json"); + let data = await axios.get("/admin-ng/resources/SERIES.json"); - const response = await data.data; + const response = await data.data; - const seriesCollection = []; - for (const series of transformToIdValueArray(response)) { - seriesCollection.push({ value: series.id, name: series.value }); - } + const seriesCollection = []; + for (const series of transformToIdValueArray(response)) { + seriesCollection.push({ value: series.id, name: series.value }); + } - return seriesCollection; + return seriesCollection; }; // Check if a series has events export const hasEvents = async (seriesId: Series["id"]) => { - let data = await axios.get(`/admin-ng/series/${seriesId}/hasEvents.json`); + let data = await axios.get(`/admin-ng/series/${seriesId}/hasEvents.json`); - return (await data.data).hasEvents; + return (await data.data).hasEvents; }; // Get series configuration and flag indicating if series with events is allowed to delete export const getSeriesConfig = async () => { - let data = await axios.get("/admin-ng/series/configuration.json"); + let data = await axios.get("/admin-ng/series/configuration.json"); - const response = await data.data; + const response = await data.data; - return !!response.deleteSeriesWithEventsAllowed; + return !!response.deleteSeriesWithEventsAllowed; }; const seriesSlice = createSlice({ - name: 'series', - initialState, - reducers: { - setSeriesColumns(state, action: PayloadAction< - SeriesState["columns"] - >) { - state.columns = action.payload; - }, - showActionsSeries(state, action: PayloadAction< - SeriesState["showActions"] - >) { - state.showActions = action.payload; - }, - setSeriesDeletionAllowed(state, action: PayloadAction<{ - deletionAllowed: SeriesState["deletionAllowed"], - hasEvents: SeriesState["hasEvents"], - }>) { - state.deletionAllowed = action.payload.deletionAllowed; - state.hasEvents = action.payload.hasEvents; - }, - setTobiraPage(state, action: PayloadAction< - SeriesState["tobiraPage"] - >) { - state.tobiraPage = action.payload; - }, - setErrorTobiraPage(state, action: PayloadAction< - SeriesState["errorTobiraPage"] - >) { - state.errorTobiraPage = action.payload; - } - }, - // These are used for thunks - extraReducers: builder => { - builder - .addCase(fetchSeries.pending, (state) => { - state.status = 'loading'; - }) - .addCase(fetchSeries.fulfilled, (state, action: PayloadAction<{ - total: SeriesState["total"], - count: SeriesState["count"], - limit: SeriesState["limit"], - offset: SeriesState["offset"], - results: SeriesState["results"], - }>) => { - state.status = 'succeeded'; - const series = action.payload; - state.total = series.total; - state.count = series.count; - state.limit = series.limit; - state.offset = series.offset; - state.results = series.results; - }) - .addCase(fetchSeries.rejected, (state, action) => { - state.status = 'failed'; - state.error = action.error; - }) - .addCase(fetchSeriesMetadata.pending, (state) => { - state.statusMetadata = 'loading'; - }) - .addCase(fetchSeriesMetadata.fulfilled, (state, action: PayloadAction<{ - metadata: SeriesState["metadata"], - extendedMetadata: SeriesState["extendedMetadata"], - }>) => { - state.statusMetadata = 'succeeded'; - const seriesMetadata = action.payload; - state.metadata = seriesMetadata.metadata; - state.extendedMetadata = seriesMetadata.extendedMetadata; - }) - .addCase(fetchSeriesMetadata.rejected, (state, action) => { - state.statusMetadata = 'failed'; - state.extendedMetadata = []; - state.errorMetadata = action.error; - }) - .addCase(fetchSeriesThemes.pending, (state) => { - state.statusThemes = 'loading'; - }) - .addCase(fetchSeriesThemes.fulfilled, (state, action: PayloadAction< - SeriesState["themes"] - >) => { - state.statusThemes = 'succeeded'; - const seriesThemes = action.payload; - state.themes = seriesThemes; - }) - .addCase(fetchSeriesThemes.rejected, (state, action) => { - state.statusThemes = 'failed'; - state.errorThemes = action.error; - }) - .addCase(fetchSeriesDetailsTobiraNew.pending, (state) => { - state.statusTobiraPage = 'loading'; - }) - .addCase(fetchSeriesDetailsTobiraNew.fulfilled, (state, action: PayloadAction< - SeriesState["tobiraPage"] - >) => { - state.statusTobiraPage = 'succeeded'; - state.tobiraPage = action.payload; - }) - .addCase(fetchSeriesDetailsTobiraNew.rejected, (state, action) => { - state.statusTobiraPage = 'failed'; - state.errorTobiraPage = action.error; - }); - } + name: 'series', + initialState, + reducers: { + setSeriesColumns(state, action: PayloadAction< + SeriesState["columns"] + >) { + state.columns = action.payload; + }, + showActionsSeries(state, action: PayloadAction< + SeriesState["showActions"] + >) { + state.showActions = action.payload; + }, + setSeriesDeletionAllowed(state, action: PayloadAction<{ + deletionAllowed: SeriesState["deletionAllowed"], + hasEvents: SeriesState["hasEvents"], + }>) { + state.deletionAllowed = action.payload.deletionAllowed; + state.hasEvents = action.payload.hasEvents; + }, + setTobiraPage(state, action: PayloadAction< + SeriesState["tobiraPage"] + >) { + state.tobiraPage = action.payload; + }, + setErrorTobiraPage(state, action: PayloadAction< + SeriesState["errorTobiraPage"] + >) { + state.errorTobiraPage = action.payload; + } + }, + // These are used for thunks + extraReducers: builder => { + builder + .addCase(fetchSeries.pending, (state) => { + state.status = 'loading'; + }) + .addCase(fetchSeries.fulfilled, (state, action: PayloadAction<{ + total: SeriesState["total"], + count: SeriesState["count"], + limit: SeriesState["limit"], + offset: SeriesState["offset"], + results: SeriesState["results"], + }>) => { + state.status = 'succeeded'; + const series = action.payload; + state.total = series.total; + state.count = series.count; + state.limit = series.limit; + state.offset = series.offset; + state.results = series.results; + }) + .addCase(fetchSeries.rejected, (state, action) => { + state.status = 'failed'; + state.error = action.error; + }) + .addCase(fetchSeriesMetadata.pending, (state) => { + state.statusMetadata = 'loading'; + }) + .addCase(fetchSeriesMetadata.fulfilled, (state, action: PayloadAction<{ + metadata: SeriesState["metadata"], + extendedMetadata: SeriesState["extendedMetadata"], + }>) => { + state.statusMetadata = 'succeeded'; + const seriesMetadata = action.payload; + state.metadata = seriesMetadata.metadata; + state.extendedMetadata = seriesMetadata.extendedMetadata; + }) + .addCase(fetchSeriesMetadata.rejected, (state, action) => { + state.statusMetadata = 'failed'; + state.extendedMetadata = []; + state.errorMetadata = action.error; + }) + .addCase(fetchSeriesThemes.pending, (state) => { + state.statusThemes = 'loading'; + }) + .addCase(fetchSeriesThemes.fulfilled, (state, action: PayloadAction< + SeriesState["themes"] + >) => { + state.statusThemes = 'succeeded'; + const seriesThemes = action.payload; + state.themes = seriesThemes; + }) + .addCase(fetchSeriesThemes.rejected, (state, action) => { + state.statusThemes = 'failed'; + state.errorThemes = action.error; + }) + .addCase(fetchSeriesDetailsTobiraNew.pending, (state) => { + state.statusTobiraPage = 'loading'; + }) + .addCase(fetchSeriesDetailsTobiraNew.fulfilled, (state, action: PayloadAction< + SeriesState["tobiraPage"] + >) => { + state.statusTobiraPage = 'succeeded'; + state.tobiraPage = action.payload; + }) + .addCase(fetchSeriesDetailsTobiraNew.rejected, (state, action) => { + state.statusTobiraPage = 'failed'; + state.errorTobiraPage = action.error; + }); + } }); export const { - setSeriesColumns, - showActionsSeries, - setSeriesDeletionAllowed, - setTobiraPage, - setErrorTobiraPage, + setSeriesColumns, + showActionsSeries, + setSeriesDeletionAllowed, + setTobiraPage, + setErrorTobiraPage, } = seriesSlice.actions; // Export the slice reducer as the default export diff --git a/src/slices/serverSlice.ts b/src/slices/serverSlice.ts index fc86b7b459..c6479835ce 100644 --- a/src/slices/serverSlice.ts +++ b/src/slices/serverSlice.ts @@ -9,112 +9,112 @@ import { createAppAsyncThunk } from '../createAsyncThunkWithTypes'; * This file contains redux reducer for actions affecting the state of servers */ export type Server = { - cores: number, - hostname: string, - maintenance: boolean, - nodeName: string, - online: boolean, - queued: number, - running: number + cores: number, + hostname: string, + maintenance: boolean, + nodeName: string, + online: boolean, + queued: number, + running: number } type ServerState = { - status: 'uninitialized' | 'loading' | 'succeeded' | 'failed', - error: SerializedError | null, - results: Server[], - columns: TableConfig["columns"], - total: number, - count: number, - offset: number, - limit: number, + status: 'uninitialized' | 'loading' | 'succeeded' | 'failed', + error: SerializedError | null, + results: Server[], + columns: TableConfig["columns"], + total: number, + count: number, + offset: number, + limit: number, } // Fill columns initially with columns defined in serversTableConfig const initialColumns = serversTableConfig.columns.map((column) => ({ - ...column, - deactivated: false, + ...column, + deactivated: false, })); // Initial state of servers in redux store const initialState: ServerState = { - status: 'uninitialized', - error: null, - results: [], - columns: initialColumns, - total: 0, - count: 0, - offset: 0, - limit: 0, + status: 'uninitialized', + error: null, + results: [], + columns: initialColumns, + total: 0, + count: 0, + offset: 0, + limit: 0, }; // fetch servers from server export const fetchServers = createAppAsyncThunk('servers/fetchServers', async (_, { getState }) => { - const state = getState(); - let params = getURLParams(state, "servers"); - // Just make the async request here, and return the response. - // This will automatically dispatch a `pending` action first, - // and then `fulfilled` or `rejected` actions based on the promise. + const state = getState(); + let params = getURLParams(state, "servers"); + // Just make the async request here, and return the response. + // This will automatically dispatch a `pending` action first, + // and then `fulfilled` or `rejected` actions based on the promise. // /servers.json?limit=0&offset=0&filter={filter}&sort={sort} - const res = await axios.get("/admin-ng/server/servers.json", { params: params }); - return res.data; + const res = await axios.get("/admin-ng/server/servers.json", { params: params }); + return res.data; }); // change maintenance status of a server/host export const setServerMaintenance = createAppAsyncThunk('servers/setServerMaintenance', async (params: { - host: Server["hostname"], - maintenance: Server["maintenance"] + host: Server["hostname"], + maintenance: Server["maintenance"] }) => { - const { host, maintenance } = params; - let data = new URLSearchParams(); - data.append("host", host); - data.append("maintenance", String(maintenance)); + const { host, maintenance } = params; + let data = new URLSearchParams(); + data.append("host", host); + data.append("maintenance", String(maintenance)); - axios - .post("/services/maintenance", data) - .then((response) => { - console.info(response); - }) - .catch((response) => { - console.error(response); - }); + axios + .post("/services/maintenance", data) + .then((response) => { + console.info(response); + }) + .catch((response) => { + console.error(response); + }); }); const serverSlice = createSlice({ - name: 'servers', - initialState, - reducers: { - setServerColumns(state, action: PayloadAction< - ServerState["columns"] - >) { - state.columns = action.payload; - }, - }, - // These are used for thunks - extraReducers: builder => { - builder - .addCase(fetchServers.pending, (state) => { - state.status = 'loading'; - }) - .addCase(fetchServers.fulfilled, (state, action: PayloadAction<{ - total: ServerState["total"], - count: ServerState["count"], - limit: ServerState["limit"], - offset: ServerState["offset"], - results: ServerState["results"], - }>) => { - state.status = 'succeeded'; - const servers = action.payload; - state.total = servers.total; - state.count = servers.count; - state.limit = servers.limit; - state.offset = servers.offset; - state.results = servers.results; - }) - .addCase(fetchServers.rejected, (state, action) => { - state.status = 'failed'; - state.error = action.error; - }); - } + name: 'servers', + initialState, + reducers: { + setServerColumns(state, action: PayloadAction< + ServerState["columns"] + >) { + state.columns = action.payload; + }, + }, + // These are used for thunks + extraReducers: builder => { + builder + .addCase(fetchServers.pending, (state) => { + state.status = 'loading'; + }) + .addCase(fetchServers.fulfilled, (state, action: PayloadAction<{ + total: ServerState["total"], + count: ServerState["count"], + limit: ServerState["limit"], + offset: ServerState["offset"], + results: ServerState["results"], + }>) => { + state.status = 'succeeded'; + const servers = action.payload; + state.total = servers.total; + state.count = servers.count; + state.limit = servers.limit; + state.offset = servers.offset; + state.results = servers.results; + }) + .addCase(fetchServers.rejected, (state, action) => { + state.status = 'failed'; + state.error = action.error; + }); + } }); export const { setServerColumns } = serverSlice.actions; diff --git a/src/slices/serviceSlice.ts b/src/slices/serviceSlice.ts index 266340f124..325d0c9b00 100644 --- a/src/slices/serviceSlice.ts +++ b/src/slices/serviceSlice.ts @@ -9,113 +9,113 @@ import { createAppAsyncThunk } from '../createAsyncThunkWithTypes'; * This file contains redux reducer for actions affecting the state of services */ export type Service = { - completed: number, - hostname: string, - meanQueueTime: number, - meanRunTime: number, - name: string, - nodeName: string, - queued: number, - running: number, - status: string, + completed: number, + hostname: string, + meanQueueTime: number, + meanRunTime: number, + name: string, + nodeName: string, + queued: number, + running: number, + status: string, } type ServiceState = { - status: 'uninitialized' | 'loading' | 'succeeded' | 'failed', - error: SerializedError | null, - results: Service[], - columns: TableConfig["columns"], - total: number, - count: number, - offset: number, - limit: number, + status: 'uninitialized' | 'loading' | 'succeeded' | 'failed', + error: SerializedError | null, + results: Service[], + columns: TableConfig["columns"], + total: number, + count: number, + offset: number, + limit: number, } // Fill columns initially with columns defined in servicesTableConfig const initialColumns = servicesTableConfig.columns.map((column) => ({ - ...column, - deactivated: false, + ...column, + deactivated: false, })); // Initial state of services in redux store const initialState: ServiceState = { - status: 'uninitialized', - error: null, - results: [], - columns: initialColumns, - total: 0, - count: 0, - offset: 0, - limit: 0, + status: 'uninitialized', + error: null, + results: [], + columns: initialColumns, + total: 0, + count: 0, + offset: 0, + limit: 0, }; // fetch services from server export const fetchServices = createAppAsyncThunk('services/fetchServices', async (_, { getState }) => { - const state = getState(); - let params = getURLParams(state, "services"); - // Just make the async request here, and return the response. - // This will automatically dispatch a `pending` action first, - // and then `fulfilled` or `rejected` actions based on the promise. - const res = await axios.get("/admin-ng/services/services.json", { params: params }); - return res.data; + const state = getState(); + let params = getURLParams(state, "services"); + // Just make the async request here, and return the response. + // This will automatically dispatch a `pending` action first, + // and then `fulfilled` or `rejected` actions based on the promise. + const res = await axios.get("/admin-ng/services/services.json", { params: params }); + return res.data; }); // restarts a service after initiated by user export const restartService = createAppAsyncThunk('services/fetchServices', async (params: { - host: Service["hostname"], - serviceType: string + host: Service["hostname"], + serviceType: string }) => { - const { host, serviceType } = params - let data = new URLSearchParams(); - data.append("host", host); - data.append("serviceType", serviceType); + const { host, serviceType } = params + let data = new URLSearchParams(); + data.append("host", host); + data.append("serviceType", serviceType); - axios - .post("/services/sanitize", data) - .then((response) => { - console.log(response); - }) - .catch((response) => { - console.log(response); - }); + axios + .post("/services/sanitize", data) + .then((response) => { + console.log(response); + }) + .catch((response) => { + console.log(response); + }); }); const serviceSlice = createSlice({ - name: 'services', - initialState, - reducers: { - setServiceColumns(state, action: PayloadAction< - ServiceState["columns"] - >) { - state.columns = action.payload; - }, - }, - // These are used for thunks - extraReducers: builder => { - builder - .addCase(fetchServices.pending, (state) => { - state.status = 'loading'; - }) - .addCase(fetchServices.fulfilled, (state, action: PayloadAction<{ - total: ServiceState["total"], - count: ServiceState["count"], - limit: ServiceState["limit"], - offset: ServiceState["offset"], - results: ServiceState["results"], - }>) => { - state.status = 'succeeded'; - const acls = action.payload; - state.total = acls.total; - state.count = acls.count; - state.limit = acls.limit; - state.offset = acls.offset; - state.results = acls.results; - }) - .addCase(fetchServices.rejected, (state, action) => { - state.status = 'failed'; - state.error = action.error; - }); - } + name: 'services', + initialState, + reducers: { + setServiceColumns(state, action: PayloadAction< + ServiceState["columns"] + >) { + state.columns = action.payload; + }, + }, + // These are used for thunks + extraReducers: builder => { + builder + .addCase(fetchServices.pending, (state) => { + state.status = 'loading'; + }) + .addCase(fetchServices.fulfilled, (state, action: PayloadAction<{ + total: ServiceState["total"], + count: ServiceState["count"], + limit: ServiceState["limit"], + offset: ServiceState["offset"], + results: ServiceState["results"], + }>) => { + state.status = 'succeeded'; + const acls = action.payload; + state.total = acls.total; + state.count = acls.count; + state.limit = acls.limit; + state.offset = acls.offset; + state.results = acls.results; + }) + .addCase(fetchServices.rejected, (state, action) => { + state.status = 'failed'; + state.error = action.error; + }); + } }); export const { setServiceColumns } = serviceSlice.actions; diff --git a/src/slices/statisticsSlice.ts b/src/slices/statisticsSlice.ts index efc53f906e..021e93863c 100644 --- a/src/slices/statisticsSlice.ts +++ b/src/slices/statisticsSlice.ts @@ -2,7 +2,7 @@ import { PayloadAction, SerializedError, createSlice } from '@reduxjs/toolkit' import axios from 'axios'; import moment from "moment"; import { - createDownloadUrl, + createDownloadUrl, } from "../utils/statisticsUtils"; import { getHttpHeaders } from "../utils/resourceUtils"; import { getStatistics } from "../selectors/statisticsSelectors"; @@ -15,311 +15,311 @@ export type TimeMode = "year" | "month" | "custom" export type DataResolution = "daily" | "monthly" | "yearly" | "weekly" | "hourly" export type Statistics = { - title: string - description: string, - providerId: string, - providerType: string, - from: string, - to: string, - dataResolution: DataResolution, - timeMode: TimeMode, - csvUrl: string, // URL - values: number[], - labels: string[], - totalValue: number, + title: string + description: string, + providerId: string, + providerType: string, + from: string, + to: string, + dataResolution: DataResolution, + timeMode: TimeMode, + csvUrl: string, // URL + values: number[], + labels: string[], + totalValue: number, } type StatisticsState = { - status: 'uninitialized' | 'loading' | 'succeeded' | 'failed', - error: SerializedError | null, - statusUpdate: 'uninitialized' | 'loading' | 'succeeded' | 'failed', - errorUpdate: SerializedError | null, - statistics: Statistics[], - hasStatisticsError: boolean, + status: 'uninitialized' | 'loading' | 'succeeded' | 'failed', + error: SerializedError | null, + statusUpdate: 'uninitialized' | 'loading' | 'succeeded' | 'failed', + errorUpdate: SerializedError | null, + statistics: Statistics[], + hasStatisticsError: boolean, } // Initial state of series details in redux store const initialState: StatisticsState = { - status: 'uninitialized', - error: null, - statusUpdate: 'uninitialized', - errorUpdate: null, - statistics: [], - hasStatisticsError: false, + status: 'uninitialized', + error: null, + statusUpdate: 'uninitialized', + errorUpdate: null, + statistics: [], + hasStatisticsError: false, }; /* thunks for fetching statistics data */ export const fetchStatisticsPageStatistics = createAppAsyncThunk('statistics/fetchStatisticsPageStatistics', async (organizationId: string, { getState }) => { - // get prior statistics - const state = getState(); - const statistics = getStatistics(state); + // get prior statistics + const state = getState(); + const statistics = getStatistics(state); - return await fetchStatistics(organizationId, "organization", statistics) + return await fetchStatistics(organizationId, "organization", statistics) }); export const fetchStatisticsPageStatisticsValueUpdate = createAppAsyncThunk('statistics/fetchStatisticsPageStatisticsValueUpdate', async ( - params: { - id: string, - providerId: string, - from: string | Date, - to: string | Date, - dataResolution: DataResolution, - timeMode: TimeMode - }, { getState }) => { - const { id, providerId, from, to, dataResolution, timeMode } = params; - - // get prior statistics - const state = getState(); - const statistics = getStatistics(state); - - return await fetchStatisticsValueUpdate(id, "organization", providerId, from, to, dataResolution, timeMode, statistics) + params: { + id: string, + providerId: string, + from: string | Date, + to: string | Date, + dataResolution: DataResolution, + timeMode: TimeMode + }, { getState }) => { + const { id, providerId, from, to, dataResolution, timeMode } = params; + + // get prior statistics + const state = getState(); + const statistics = getStatistics(state); + + return await fetchStatisticsValueUpdate(id, "organization", providerId, from, to, dataResolution, timeMode, statistics) }); export const fetchStatistics = async (resourceId: string, resourceType: string, statistics: Statistics[]) => { - let hasError = false; - - // create url params - let params = new URLSearchParams(); - params.append("resourceType", resourceType); - - let newStatistics: Statistics[] = []; - const statisticsValueRequest = []; - - // get the available statistics providers from API - try { - const response = await axios.get("/admin-ng/statistics/providers.json", { params }) - // default values to use, when statistics are viewed the first time - const originalDataResolution = "monthly"; - const originalTimeMode = "year"; - const originalFrom = moment().startOf(originalTimeMode); - const originalTo = moment().endOf(originalTimeMode); - - // iterate over statistics providers - for (let i = 0; i < response.data.length; i++) { - // currently, only time series data can be displayed, for other types, add data directly, then continue - if (response.data[i].providerType !== "timeSeries") { - newStatistics.push({ - ...response.data[i], - }); - } else { - // case: provider is of type time series - let from; - let to; - let timeMode; - let dataResolution; - - /* if old values for this statistic exist, use old - from (date), to (date), timeMode and dataResolution values, otherwise use defaults */ - if (statistics.length > i) { - from = statistics[i].from; - to = statistics[i].to; - timeMode = statistics[i].timeMode; - dataResolution = statistics[i].dataResolution; - } else { - from = originalFrom.format("YYYY-MM-DD"); - to = originalTo.format("YYYY-MM-DD"); - timeMode = originalTimeMode; - dataResolution = originalDataResolution; - } - - // create chart options and download url - const csvUrl = createDownloadUrl( - resourceId, - resourceType, - response.data[i].providerId, - from, - to, - dataResolution - ); - - // add provider to statistics list and add statistic settings - newStatistics.push({ - ...response.data[i], - from: from, - to: to, - timeMode: timeMode, - dataResolution: dataResolution, - csvUrl: csvUrl, - }); - - // add settings for this statistic of this resource to value request - statisticsValueRequest.push({ - dataResolution: dataResolution, - from: moment(from), - to: moment(to).endOf("day"), - resourceId: resourceId, - providerId: response.data[i].providerId, - }); - } - } - - // prepare header and data for statistics values request - const requestHeaders = getHttpHeaders(); - const requestData = new URLSearchParams({ - data: JSON.stringify(statisticsValueRequest), - }); - - // request statistics values from API - const dataResponse = await axios.post("/admin-ng/statistics/data.json", requestData, requestHeaders) - // iterate over value responses - for (const statisticsValue of dataResponse.data) { - // get the statistic the response is meant for - const stat = newStatistics.find( - (element) => element.providerId === statisticsValue.providerId - ); - - if (!stat) { - continue; - } - - // add values to statistic - const statistic = { - ...stat, - values: statisticsValue.values, - labels: statisticsValue.labels, - totalValue: statisticsValue.total, - }; - - // put updated statistic into statistics list - newStatistics = newStatistics.map((oldStat) => - oldStat === stat ? statistic : oldStat - ); - - // put statistics list into redux store - statistics = newStatistics; - hasError = false; - } - statistics = newStatistics; - hasError = false; - } catch(leError) { - // put unfinished statistics list into redux store but set flag that an error occurred - statistics = newStatistics; - hasError = true; - console.error(leError); - } - - return { statistics, hasError }; + let hasError = false; + + // create url params + let params = new URLSearchParams(); + params.append("resourceType", resourceType); + + let newStatistics: Statistics[] = []; + const statisticsValueRequest = []; + + // get the available statistics providers from API + try { + const response = await axios.get("/admin-ng/statistics/providers.json", { params }) + // default values to use, when statistics are viewed the first time + const originalDataResolution = "monthly"; + const originalTimeMode = "year"; + const originalFrom = moment().startOf(originalTimeMode); + const originalTo = moment().endOf(originalTimeMode); + + // iterate over statistics providers + for (let i = 0; i < response.data.length; i++) { + // currently, only time series data can be displayed, for other types, add data directly, then continue + if (response.data[i].providerType !== "timeSeries") { + newStatistics.push({ + ...response.data[i], + }); + } else { + // case: provider is of type time series + let from; + let to; + let timeMode; + let dataResolution; + + /* if old values for this statistic exist, use old + from (date), to (date), timeMode and dataResolution values, otherwise use defaults */ + if (statistics.length > i) { + from = statistics[i].from; + to = statistics[i].to; + timeMode = statistics[i].timeMode; + dataResolution = statistics[i].dataResolution; + } else { + from = originalFrom.format("YYYY-MM-DD"); + to = originalTo.format("YYYY-MM-DD"); + timeMode = originalTimeMode; + dataResolution = originalDataResolution; + } + + // create chart options and download url + const csvUrl = createDownloadUrl( + resourceId, + resourceType, + response.data[i].providerId, + from, + to, + dataResolution + ); + + // add provider to statistics list and add statistic settings + newStatistics.push({ + ...response.data[i], + from: from, + to: to, + timeMode: timeMode, + dataResolution: dataResolution, + csvUrl: csvUrl, + }); + + // add settings for this statistic of this resource to value request + statisticsValueRequest.push({ + dataResolution: dataResolution, + from: moment(from), + to: moment(to).endOf("day"), + resourceId: resourceId, + providerId: response.data[i].providerId, + }); + } + } + + // prepare header and data for statistics values request + const requestHeaders = getHttpHeaders(); + const requestData = new URLSearchParams({ + data: JSON.stringify(statisticsValueRequest), + }); + + // request statistics values from API + const dataResponse = await axios.post("/admin-ng/statistics/data.json", requestData, requestHeaders) + // iterate over value responses + for (const statisticsValue of dataResponse.data) { + // get the statistic the response is meant for + const stat = newStatistics.find( + (element) => element.providerId === statisticsValue.providerId + ); + + if (!stat) { + continue; + } + + // add values to statistic + const statistic = { + ...stat, + values: statisticsValue.values, + labels: statisticsValue.labels, + totalValue: statisticsValue.total, + }; + + // put updated statistic into statistics list + newStatistics = newStatistics.map((oldStat) => + oldStat === stat ? statistic : oldStat + ); + + // put statistics list into redux store + statistics = newStatistics; + hasError = false; + } + statistics = newStatistics; + hasError = false; + } catch(leError) { + // put unfinished statistics list into redux store but set flag that an error occurred + statistics = newStatistics; + hasError = true; + console.error(leError); + } + + return { statistics, hasError }; }; export const fetchStatisticsValueUpdate = async ( - resourceId: string, - resourceType: string, - providerId: string, - from: string | Date, - to: string | Date, - dataResolution: DataResolution, - timeMode: TimeMode, - statistics: Statistics[], + resourceId: string, + resourceType: string, + providerId: string, + from: string | Date, + to: string | Date, + dataResolution: DataResolution, + timeMode: TimeMode, + statistics: Statistics[], ) => { - // settings for this statistic of this resource for value request - const statisticsValueRequest = [ - { - dataResolution: dataResolution, - from: moment(from), - to: moment(to).endOf("day"), - resourceId: resourceId, - providerId: providerId, - }, - ]; - - // prepare header and data for statistic values request - const requestHeaders = getHttpHeaders(); - const requestData = new URLSearchParams({ - data: JSON.stringify(statisticsValueRequest), - }); - - let newStatistics - // request statistic values from API - await axios - .post("/admin-ng/statistics/data.json", requestData, requestHeaders) - .then((dataResponse) => { - // if only one element is in the response (as expected), get the response - if (dataResponse.data.length === 1) { - const newStatisticData = dataResponse.data[0]; - - // get the statistic the response is meant for out of the statistics list - const stat = statistics.find( - (element) => element.providerId === providerId - ); - - // get statistic options and download url for new statistic settings - // const options = createChartOptions(timeMode, dataResolution); - const csvUrl = createDownloadUrl( - resourceId, - resourceType, - providerId, - from, - to, - dataResolution - ); - - // update statistic - const statistic = { - ...stat, - from: from, - to: to, - dataResolution: dataResolution, - timeMode: timeMode, - // options: options, - csvUrl: csvUrl, - values: newStatisticData.values, - labels: newStatisticData.labels, - totalValue: newStatisticData.total, - }; - - // put updated statistic into statistics list - newStatistics = statistics.map((oldStat) => - oldStat === stat ? statistic : oldStat - ); - } - }) - - // put updates statistics list into redux store - return newStatistics + // settings for this statistic of this resource for value request + const statisticsValueRequest = [ + { + dataResolution: dataResolution, + from: moment(from), + to: moment(to).endOf("day"), + resourceId: resourceId, + providerId: providerId, + }, + ]; + + // prepare header and data for statistic values request + const requestHeaders = getHttpHeaders(); + const requestData = new URLSearchParams({ + data: JSON.stringify(statisticsValueRequest), + }); + + let newStatistics + // request statistic values from API + await axios + .post("/admin-ng/statistics/data.json", requestData, requestHeaders) + .then((dataResponse) => { + // if only one element is in the response (as expected), get the response + if (dataResponse.data.length === 1) { + const newStatisticData = dataResponse.data[0]; + + // get the statistic the response is meant for out of the statistics list + const stat = statistics.find( + (element) => element.providerId === providerId + ); + + // get statistic options and download url for new statistic settings + // const options = createChartOptions(timeMode, dataResolution); + const csvUrl = createDownloadUrl( + resourceId, + resourceType, + providerId, + from, + to, + dataResolution + ); + + // update statistic + const statistic = { + ...stat, + from: from, + to: to, + dataResolution: dataResolution, + timeMode: timeMode, + // options: options, + csvUrl: csvUrl, + values: newStatisticData.values, + labels: newStatisticData.labels, + totalValue: newStatisticData.total, + }; + + // put updated statistic into statistics list + newStatistics = statistics.map((oldStat) => + oldStat === stat ? statistic : oldStat + ); + } + }) + + // put updates statistics list into redux store + return newStatistics }; const statisticsSlice = createSlice({ - name: 'statistics', - initialState, - reducers: {}, - // These are used for thunks - extraReducers: builder => { - builder - .addCase(fetchStatisticsPageStatistics.pending, (state) => { - state.status = 'loading'; - }) - .addCase(fetchStatisticsPageStatistics.fulfilled, (state, action: PayloadAction<{ - statistics: StatisticsState["statistics"], - hasError: StatisticsState["hasStatisticsError"] - }>) => { - state.status = 'succeeded'; - const statistics = action.payload; - state.statistics = statistics.statistics; - state.hasStatisticsError = statistics.hasError; - }) - .addCase(fetchStatisticsPageStatistics.rejected, (state, action) => { - state.status = 'failed'; - state.hasStatisticsError = true; - state.error = action.error; - }) - .addCase(fetchStatisticsPageStatisticsValueUpdate.pending, (state) => { - state.statusUpdate = 'loading'; - }) - .addCase(fetchStatisticsPageStatisticsValueUpdate.fulfilled, (state, action: PayloadAction< - any - >) => { - state.statusUpdate = 'succeeded'; - const statistics = action.payload; - state.statistics = statistics; - }) - .addCase(fetchStatisticsPageStatisticsValueUpdate.rejected, (state, action) => { - state.statusUpdate = 'failed'; - state.errorUpdate = action.error; - }); - } + name: 'statistics', + initialState, + reducers: {}, + // These are used for thunks + extraReducers: builder => { + builder + .addCase(fetchStatisticsPageStatistics.pending, (state) => { + state.status = 'loading'; + }) + .addCase(fetchStatisticsPageStatistics.fulfilled, (state, action: PayloadAction<{ + statistics: StatisticsState["statistics"], + hasError: StatisticsState["hasStatisticsError"] + }>) => { + state.status = 'succeeded'; + const statistics = action.payload; + state.statistics = statistics.statistics; + state.hasStatisticsError = statistics.hasError; + }) + .addCase(fetchStatisticsPageStatistics.rejected, (state, action) => { + state.status = 'failed'; + state.hasStatisticsError = true; + state.error = action.error; + }) + .addCase(fetchStatisticsPageStatisticsValueUpdate.pending, (state) => { + state.statusUpdate = 'loading'; + }) + .addCase(fetchStatisticsPageStatisticsValueUpdate.fulfilled, (state, action: PayloadAction< + any + >) => { + state.statusUpdate = 'succeeded'; + const statistics = action.payload; + state.statistics = statistics; + }) + .addCase(fetchStatisticsPageStatisticsValueUpdate.rejected, (state, action) => { + state.statusUpdate = 'failed'; + state.errorUpdate = action.error; + }); + } }); // export const {} = statisticsSlice.actions; diff --git a/src/slices/tableFilterProfilesSlice.ts b/src/slices/tableFilterProfilesSlice.ts index eb147eeb7e..0c7a46f6db 100644 --- a/src/slices/tableFilterProfilesSlice.ts +++ b/src/slices/tableFilterProfilesSlice.ts @@ -6,57 +6,57 @@ import { FilterData } from './tableFilterSlice' */ export type FilterProfile = { - name: string, - description: string, - resource: string, - filterMap: FilterData[] + name: string, + description: string, + resource: string, + filterMap: FilterData[] } type TableFilterProfilesState = { - profiles: FilterProfile[] + profiles: FilterProfile[] } // Initial state of filter profiles in redux store const initialState: TableFilterProfilesState = { - profiles: [] + profiles: [] }; const tableFilterProfileSlice = createSlice({ - name: 'tableFilterProfiles', - initialState, - reducers: { - createFilterProfile(state, action: PayloadAction< - FilterProfile - >) { - const filterProfile = action.payload; - state.profiles = state.profiles.concat(filterProfile) - }, - editFilterProfile(state, action: PayloadAction< - FilterProfile - >) { - const updatedFilterProfile = action.payload; - state.profiles = state.profiles.map((filterProfile) => { - if (filterProfile.name === updatedFilterProfile.name) { - return updatedFilterProfile; - } - return filterProfile; - }) - }, - removeFilterProfile(state, action: PayloadAction< - FilterProfile - >) { - const filterProfileToRemove = action.payload; - state.profiles = state.profiles.filter( - (filterProfile) => filterProfile.name !== filterProfileToRemove.name - ) - } - }, + name: 'tableFilterProfiles', + initialState, + reducers: { + createFilterProfile(state, action: PayloadAction< + FilterProfile + >) { + const filterProfile = action.payload; + state.profiles = state.profiles.concat(filterProfile) + }, + editFilterProfile(state, action: PayloadAction< + FilterProfile + >) { + const updatedFilterProfile = action.payload; + state.profiles = state.profiles.map((filterProfile) => { + if (filterProfile.name === updatedFilterProfile.name) { + return updatedFilterProfile; + } + return filterProfile; + }) + }, + removeFilterProfile(state, action: PayloadAction< + FilterProfile + >) { + const filterProfileToRemove = action.payload; + state.profiles = state.profiles.filter( + (filterProfile) => filterProfile.name !== filterProfileToRemove.name + ) + } + }, }); export const { - createFilterProfile, - editFilterProfile, - removeFilterProfile, + createFilterProfile, + editFilterProfile, + removeFilterProfile, } = tableFilterProfileSlice.actions; // Export the slice reducer as the default export diff --git a/src/slices/tableFilterSlice.ts b/src/slices/tableFilterSlice.ts index 163c4d7693..2371b25e57 100644 --- a/src/slices/tableFilterSlice.ts +++ b/src/slices/tableFilterSlice.ts @@ -13,382 +13,382 @@ import { FilterProfile } from './tableFilterProfilesSlice'; */ export type FilterData = { - label: string, - name: string, - options?: { - label: string, - value: string, - }[], - translatable: boolean, - type: string, - resource: string, // Not from the backend. We set this to keep track of which table this filter belongs to - value: string, + label: string, + name: string, + options?: { + label: string, + value: string, + }[], + translatable: boolean, + type: string, + resource: string, // Not from the backend. We set this to keep track of which table this filter belongs to + value: string, } export type Stats = { - count: number, - description: string, - filters: { - filter: string, - name: string, - value: string, - }[], - name: string, - order: number, + count: number, + description: string, + filters: { + filter: string, + name: string, + value: string, + }[], + name: string, + order: number, } type TableFilterState = { - status: 'uninitialized' | 'loading' | 'succeeded' | 'failed', - error: SerializedError | null, - statusStats: 'uninitialized' | 'loading' | 'succeeded' | 'failed', - errorStats: SerializedError | null, - currentResource: string, - data: FilterData[], - filterProfiles: FilterProfile[], - textFilter: string, - selectedFilter: string, - secondFilter: string, - stats: Stats[], + status: 'uninitialized' | 'loading' | 'succeeded' | 'failed', + error: SerializedError | null, + statusStats: 'uninitialized' | 'loading' | 'succeeded' | 'failed', + errorStats: SerializedError | null, + currentResource: string, + data: FilterData[], + filterProfiles: FilterProfile[], + textFilter: string, + selectedFilter: string, + secondFilter: string, + stats: Stats[], } // Initial state of table filters in redux store const initialState: TableFilterState = { - status: 'uninitialized', - error: null, - statusStats: 'uninitialized', - errorStats: null, - currentResource: "", - data: [], - filterProfiles: [], - textFilter: "", - selectedFilter: "", - secondFilter: "", - stats: [], + status: 'uninitialized', + error: null, + statusStats: 'uninitialized', + errorStats: null, + currentResource: "", + data: [], + filterProfiles: [], + textFilter: "", + selectedFilter: "", + secondFilter: "", + stats: [], }; // Fetch table filters from opencast instance and transform them for further use export const fetchFilters = createAppAsyncThunk('tableFilters/fetchFilters', async (resource: TableFilterState["currentResource"], { getState }) => { - const data = await axios.get( - `/admin-ng/resources/${resource}/filters.json` - ); - const resourceData = await data.data; - - const filters = transformResponse(resourceData); - const filtersList = Object.keys(filters.filters).map((key) => { - let filter = filters.filters[key]; - filter.name = key; - filter.resource = resource; - return filter; - }); - - if (resource === "events") { - filtersList.push({ - label: "FILTERS.EVENTS.PRESENTERS_BIBLIOGRAPHIC.LABEL", - name: "presentersBibliographic", - translatable: false, - type: "select", - resource: "events", - value: "", - }); - } - - // Do all this purely to keep set filter values saved if the tab gets switched - let oldData = getState().tableFilters.data - - for (const oldFilter of oldData) { - var foundIndex = filtersList.findIndex(x => x.name === oldFilter.name && x.resource === oldFilter.resource); - if (foundIndex >= 0) { - filtersList[foundIndex].value = oldFilter.value; - } - } - - oldData = oldData.filter(filter => filter.resource !== resource) - filtersList.push(...oldData) - - return { filtersList, resource }; + const data = await axios.get( + `/admin-ng/resources/${resource}/filters.json` + ); + const resourceData = await data.data; + + const filters = transformResponse(resourceData); + const filtersList = Object.keys(filters.filters).map((key) => { + let filter = filters.filters[key]; + filter.name = key; + filter.resource = resource; + return filter; + }); + + if (resource === "events") { + filtersList.push({ + label: "FILTERS.EVENTS.PRESENTERS_BIBLIOGRAPHIC.LABEL", + name: "presentersBibliographic", + translatable: false, + type: "select", + resource: "events", + value: "", + }); + } + + // Do all this purely to keep set filter values saved if the tab gets switched + let oldData = getState().tableFilters.data + + for (const oldFilter of oldData) { + var foundIndex = filtersList.findIndex(x => x.name === oldFilter.name && x.resource === oldFilter.resource); + if (foundIndex >= 0) { + filtersList[foundIndex].value = oldFilter.value; + } + } + + oldData = oldData.filter(filter => filter.resource !== resource) + filtersList.push(...oldData) + + return { filtersList, resource }; }); export const fetchStats = createAppAsyncThunk('tableFilters/fetchStats', async () => { - // fetch information about possible status an event can have - let data = await axios.get("/admin-ng/resources/STATS.json"); - let response = await data.data; - - // transform response - const statsResponse = Object.keys(response).map((key) => { - let stat = JSON.parse(response[key]); - stat.name = key; - return stat; - }); - - let stats = []; - - // fetch for each status the corresponding count of events having this status - for (let i in statsResponse) { - let filter = []; - for (let j in statsResponse[i].filters) { - let value = statsResponse[i].filters[j].value; - let name = statsResponse[i].filters[j].name; - - if (Object.prototype.hasOwnProperty.call(value, "relativeDateSpan")) { - value = relativeDateSpanToFilterValue( - value.relativeDateSpan.from, - value.relativeDateSpan.to, - value.relativeDateSpan.unit - ); - // set date span as filter value - statsResponse[i].filters[j].value = value; - } - filter.push(name + ":" + value); - } - let data = await axios.get("/admin-ng/event/events.json", { - params: { - filter: filter.join(","), - limit: 1, - }, - }); - - let response = await data.data; - - // add count to status information fetched before - statsResponse[i] = { - ...statsResponse[i], - count: response.total, - }; - - // fill stats array for redux state - stats.push(statsResponse[i]); - } - - stats.sort(compareOrder); - - return stats; + // fetch information about possible status an event can have + let data = await axios.get("/admin-ng/resources/STATS.json"); + let response = await data.data; + + // transform response + const statsResponse = Object.keys(response).map((key) => { + let stat = JSON.parse(response[key]); + stat.name = key; + return stat; + }); + + let stats = []; + + // fetch for each status the corresponding count of events having this status + for (let i in statsResponse) { + let filter = []; + for (let j in statsResponse[i].filters) { + let value = statsResponse[i].filters[j].value; + let name = statsResponse[i].filters[j].name; + + if (Object.prototype.hasOwnProperty.call(value, "relativeDateSpan")) { + value = relativeDateSpanToFilterValue( + value.relativeDateSpan.from, + value.relativeDateSpan.to, + value.relativeDateSpan.unit + ); + // set date span as filter value + statsResponse[i].filters[j].value = value; + } + filter.push(name + ":" + value); + } + let data = await axios.get("/admin-ng/event/events.json", { + params: { + filter: filter.join(","), + limit: 1, + }, + }); + + let response = await data.data; + + // add count to status information fetched before + statsResponse[i] = { + ...statsResponse[i], + count: response.total, + }; + + // fill stats array for redux state + stats.push(statsResponse[i]); + } + + stats.sort(compareOrder); + + return stats; }); export const setSpecificEventFilter = createAppAsyncThunk('tableFilters/setSpecificEventFilter', async (params: { filter: string, filterValue: string }, { dispatch, getState }) => { - const { filter, filterValue } = params; - await dispatch(fetchFilters("events")); + const { filter, filterValue } = params; + await dispatch(fetchFilters("events")); - const { tableFilters } = getState(); + const { tableFilters } = getState(); - let filterToChange = tableFilters.data.find(({ name }) => name === filter); + let filterToChange = tableFilters.data.find(({ name }) => name === filter); - if (!!filterToChange) { - await dispatch(editFilterValue({ - filterName: filterToChange.name, - value: filterValue - })); - } + if (!!filterToChange) { + await dispatch(editFilterValue({ + filterName: filterToChange.name, + value: filterValue + })); + } - dispatch(setOffset(0)); + dispatch(setOffset(0)); - dispatch(fetchStats()); + dispatch(fetchStats()); - dispatch(fetchEvents()); + dispatch(fetchEvents()); }); export const setSpecificServiceFilter = createAppAsyncThunk('tableFilters/setSpecificServiceFilter', async (params: { filter: string, filterValue: string }, { dispatch, getState }) => { - const { filter, filterValue } = params; - await dispatch(fetchFilters("services")); + const { filter, filterValue } = params; + await dispatch(fetchFilters("services")); - const { tableFilters } = getState(); + const { tableFilters } = getState(); - let filterToChange = tableFilters.data.find(({ name }) => name === filter); + let filterToChange = tableFilters.data.find(({ name }) => name === filter); - if (!!filterToChange) { - await dispatch(editFilterValue({ - filterName: filterToChange.name, - value: filterValue - })); - } + if (!!filterToChange) { + await dispatch(editFilterValue({ + filterName: filterToChange.name, + value: filterValue + })); + } - dispatch(setOffset(0)); + dispatch(setOffset(0)); - dispatch(fetchServices()); + dispatch(fetchServices()); }); // Transform received filter.json to a structure that can be used for filtering function transformResponse(data: { - [key: string]: { - value: string, - label: string, - options?: { [key: string]: string }, - name: string - translatable: boolean, - type: string, - resource: string, - } + [key: string]: { + value: string, + label: string, + options?: { [key: string]: string }, + name: string + translatable: boolean, + type: string, + resource: string, + } }) { - type ParsedFilters = { - [key: string]: { - value: string - label: string - options?: { value: string, label: string }[] - name: string - translatable: boolean, - type: string, - resource: string, - } - } - - let filters = Object.keys(data).reduce((acc, key) => { - let newOptions: { - label: string, - value: string, - }[] = [] - acc[key] = { - ...data[key], - options: newOptions - }; - return acc; - }, {} as ParsedFilters); - - try { - for (let key in data) { - filters[key].value = ""; - if (!data[key].options) { - continue; - } - let filterArr: { value: string, label: string }[] = []; - let options = data[key].options; - for (let subKey in options) { - filterArr.push({ value: subKey, label: options[subKey] }); - } - filterArr = filterArr.sort(function (a, b) { - if (a.label.toLowerCase() < b.label.toLowerCase()) { - return -1; - } - if (a.label.toLowerCase() > b.label.toLowerCase()) { - return 1; - } - return 0; - }); - filters[key].options = filterArr; - } - } catch (e) { - let errorMessage; - if (e instanceof Error) { - errorMessage = e.message - } else { - errorMessage = String(e); - } - console.error(errorMessage); - } - - return { filters: filters }; + type ParsedFilters = { + [key: string]: { + value: string + label: string + options?: { value: string, label: string }[] + name: string + translatable: boolean, + type: string, + resource: string, + } + } + + let filters = Object.keys(data).reduce((acc, key) => { + let newOptions: { + label: string, + value: string, + }[] = [] + acc[key] = { + ...data[key], + options: newOptions + }; + return acc; + }, {} as ParsedFilters); + + try { + for (let key in data) { + filters[key].value = ""; + if (!data[key].options) { + continue; + } + let filterArr: { value: string, label: string }[] = []; + let options = data[key].options; + for (let subKey in options) { + filterArr.push({ value: subKey, label: options[subKey] }); + } + filterArr = filterArr.sort(function (a, b) { + if (a.label.toLowerCase() < b.label.toLowerCase()) { + return -1; + } + if (a.label.toLowerCase() > b.label.toLowerCase()) { + return 1; + } + return 0; + }); + filters[key].options = filterArr; + } + } catch (e) { + let errorMessage; + if (e instanceof Error) { + errorMessage = e.message + } else { + errorMessage = String(e); + } + console.error(errorMessage); + } + + return { filters: filters }; } // compare function for sort stats array by order property const compareOrder = (a: { order: number }, b: { order: number }) => { - if (a.order < b.order) { - return -1; - } - if (a.order > b.order) { - return 1; - } - return 0; + if (a.order < b.order) { + return -1; + } + if (a.order > b.order) { + return 1; + } + return 0; }; const tableFilterSlice = createSlice({ - name: 'tableFilters', - initialState, - reducers: { - editFilterValue(state, action: PayloadAction<{ - filterName: TableFilterState["data"][0]["name"], - value: TableFilterState["data"][0]["value"], - }>) { - const { filterName, value } = action.payload; - state.data = state.data.map((filter) => { - return filter.name === filterName - ? { ...filter, value: value } - : filter; - }) - }, - resetFilterValues(state) { - state.data = state.data.map((filter) => { - return { ...filter, value: "" }; - }) - }, - editTextFilter(state, action: PayloadAction< - TableFilterState["textFilter"] - >) { - const textFilter = action.payload; - state.textFilter = textFilter; - }, - removeTextFilter(state) { - state.textFilter = ""; - }, - loadFilterProfile(state, action: PayloadAction< - TableFilterState["data"] - >) { - const filterMap = action.payload; - state.data = filterMap; - }, - editSelectedFilter(state, action: PayloadAction< - TableFilterState["selectedFilter"] - >) { - const filter = action.payload; - state.selectedFilter = filter; - }, - removeSelectedFilter(state) { - state.selectedFilter = ""; - }, - editSecondFilter(state, action: PayloadAction< - TableFilterState["secondFilter"] - >) { - const filter = action.payload; - state.secondFilter = filter; - }, - removeSecondFilter(state) { - state.secondFilter = ""; - }, - }, - extraReducers: builder => { - builder - .addCase(fetchFilters.pending, (state) => { - state.status = 'loading'; - }) - .addCase(fetchFilters.fulfilled, (state, action: PayloadAction<{ - filtersList: TableFilterState["data"], - resource: TableFilterState["currentResource"], - }>) => { - state.status = 'succeeded'; - const tableFilters = action.payload; - state.data = tableFilters.filtersList; - state.currentResource = tableFilters.resource; - - }) - .addCase(fetchFilters.rejected, (state, action) => { - state.status = 'failed'; - state.error = action.error; - }) - .addCase(fetchStats.pending, (state) => { - state.statusStats = 'loading'; - }) - .addCase(fetchStats.fulfilled, (state, action: PayloadAction< - TableFilterState["stats"] - >) => { - state.statusStats = 'succeeded'; - const stats = action.payload; - state.stats = stats; - }) - .addCase(fetchStats.rejected, (state, action) => { - state.statusStats = 'failed'; - state.errorStats = action.error; - }); - } + name: 'tableFilters', + initialState, + reducers: { + editFilterValue(state, action: PayloadAction<{ + filterName: TableFilterState["data"][0]["name"], + value: TableFilterState["data"][0]["value"], + }>) { + const { filterName, value } = action.payload; + state.data = state.data.map((filter) => { + return filter.name === filterName + ? { ...filter, value: value } + : filter; + }) + }, + resetFilterValues(state) { + state.data = state.data.map((filter) => { + return { ...filter, value: "" }; + }) + }, + editTextFilter(state, action: PayloadAction< + TableFilterState["textFilter"] + >) { + const textFilter = action.payload; + state.textFilter = textFilter; + }, + removeTextFilter(state) { + state.textFilter = ""; + }, + loadFilterProfile(state, action: PayloadAction< + TableFilterState["data"] + >) { + const filterMap = action.payload; + state.data = filterMap; + }, + editSelectedFilter(state, action: PayloadAction< + TableFilterState["selectedFilter"] + >) { + const filter = action.payload; + state.selectedFilter = filter; + }, + removeSelectedFilter(state) { + state.selectedFilter = ""; + }, + editSecondFilter(state, action: PayloadAction< + TableFilterState["secondFilter"] + >) { + const filter = action.payload; + state.secondFilter = filter; + }, + removeSecondFilter(state) { + state.secondFilter = ""; + }, + }, + extraReducers: builder => { + builder + .addCase(fetchFilters.pending, (state) => { + state.status = 'loading'; + }) + .addCase(fetchFilters.fulfilled, (state, action: PayloadAction<{ + filtersList: TableFilterState["data"], + resource: TableFilterState["currentResource"], + }>) => { + state.status = 'succeeded'; + const tableFilters = action.payload; + state.data = tableFilters.filtersList; + state.currentResource = tableFilters.resource; + + }) + .addCase(fetchFilters.rejected, (state, action) => { + state.status = 'failed'; + state.error = action.error; + }) + .addCase(fetchStats.pending, (state) => { + state.statusStats = 'loading'; + }) + .addCase(fetchStats.fulfilled, (state, action: PayloadAction< + TableFilterState["stats"] + >) => { + state.statusStats = 'succeeded'; + const stats = action.payload; + state.stats = stats; + }) + .addCase(fetchStats.rejected, (state, action) => { + state.statusStats = 'failed'; + state.errorStats = action.error; + }); + } }); export const { - editFilterValue, - resetFilterValues, - editTextFilter, - removeTextFilter, - loadFilterProfile, - editSelectedFilter, - removeSelectedFilter, - editSecondFilter, - removeSecondFilter + editFilterValue, + resetFilterValues, + editTextFilter, + removeTextFilter, + loadFilterProfile, + editSelectedFilter, + removeSelectedFilter, + editSecondFilter, + removeSecondFilter } = tableFilterSlice.actions; // Export the slice reducer as the default export diff --git a/src/slices/tableSlice.ts b/src/slices/tableSlice.ts index 9029db8e68..a832dc974c 100644 --- a/src/slices/tableSlice.ts +++ b/src/slices/tableSlice.ts @@ -14,24 +14,24 @@ import { Event } from './eventSlice'; /* Overview of the structure of the data in arrays in state const pages = [{ - active: false, - label: "", - number: 1 + active: false, + label: "", + number: 1 }, ...] const rows = [{ - id: 1, - data: [{for each column a value}] + id: 1, + data: [{for each column a value}] }, ...] const columns = [{ - style: "", - deactivated: true, - name: "", - sortable: false, - label: "", - translate: false, - template: "" + style: "", + deactivated: true, + name: "", + sortable: false, + label: "", + translate: false, + template: "" }, ...] */ @@ -40,31 +40,31 @@ const columns = [{ */ export type Page = { - active: boolean, - label: string, - number: number, + active: boolean, + label: string, + number: number, }; export type Pagination = { - limit: number, - offset: number, - totalItems: number, - directAccessibleNo: number, + limit: number, + offset: number, + totalItems: number, + directAccessibleNo: number, } export function isRowSelectable(row: Row) { - if ("id" in row === true) { - return true; - } - return false; + if ("id" in row === true) { + return true; + } + return false; } export function isEvent(row: Event | Series | Recording | Server | Job | Service | User | Group | AclResult | ThemeDetailsType): row is Event { - return (row as Event).event_status !== undefined; + return (row as Event).event_status !== undefined; } export function isSeries(row: Row | Event | Series | Recording | Server | Job | Service | User | Group | AclResult | ThemeDetailsType): row is Series { - return (row as Series).organizers !== undefined; + return (row as Series).organizers !== undefined; } // TODO: Improve row typing. While this somewhat correctly reflects the current state of our code, it is rather annoying to work with. @@ -73,212 +73,212 @@ export type Row = { selected: boolean } & ( Event | Series | Recording | Server export type Resource = "events" | "series" | "recordings" | "jobs" | "servers" | "services" | "users" | "groups" | "acls" | "themes" export type TableState = { - status: 'uninitialized' | 'loading' | 'succeeded' | 'failed', - error: SerializedError | null, - multiSelect: boolean, - resource: Resource, - pages: Page[], - columns: TableConfig["columns"], - sortBy: { [key: string]: string }, // Key is resource, value is actual sorting parameter - predicate: string, - reverse: { [key: string]: string }, // Key is resource, value is actual sorting parameter - rows: Row[], - maxLabel: string, - pagination: Pagination, + status: 'uninitialized' | 'loading' | 'succeeded' | 'failed', + error: SerializedError | null, + multiSelect: boolean, + resource: Resource, + pages: Page[], + columns: TableConfig["columns"], + sortBy: { [key: string]: string }, // Key is resource, value is actual sorting parameter + predicate: string, + reverse: { [key: string]: string }, // Key is resource, value is actual sorting parameter + rows: Row[], + maxLabel: string, + pagination: Pagination, } // initial redux state const initialState: TableState = { - status: 'uninitialized', - error: null, - multiSelect: false, - resource: "events", - pages: [], - columns: [], - sortBy: { - events: "date", - series: "createdDateTime", - recordings: "status", - jobs: "id", - servers: "online", - services: "status", - users: "name", - groups: "name", - acls: "name", - themes: "name", - }, - predicate: "", - reverse: { - events: "DESC", - series: "DESC", - recordings: "ASC", - jobs: "ASC", - servers: "ASC", - services: "ASC", - users: "ASC", - groups: "ASC", - acls: "ASC", - theme: "ASC", - }, - rows: [], - maxLabel: "", - pagination: { - limit: 10, - offset: 0, - totalItems: 0, - directAccessibleNo: 3, - }, + status: 'uninitialized', + error: null, + multiSelect: false, + resource: "events", + pages: [], + columns: [], + sortBy: { + events: "date", + series: "createdDateTime", + recordings: "status", + jobs: "id", + servers: "online", + services: "status", + users: "name", + groups: "name", + acls: "name", + themes: "name", + }, + predicate: "", + reverse: { + events: "DESC", + series: "DESC", + recordings: "ASC", + jobs: "ASC", + servers: "ASC", + services: "ASC", + users: "ASC", + groups: "ASC", + acls: "ASC", + theme: "ASC", + }, + rows: [], + maxLabel: "", + pagination: { + limit: 10, + offset: 0, + totalItems: 0, + directAccessibleNo: 3, + }, }; const tableSlice = createSlice({ - name: 'table', - initialState, - reducers: { - loadResourceIntoTable(state, action: PayloadAction<{ - multiSelect: TableState["multiSelect"], - columns: TableConfig["columns"], - resource: TableState["resource"], - pages: TableState["pages"], - rows: TableState["rows"], - sortBy: TableState["sortBy"][0], - reverse: TableState["reverse"][0], - totalItems: TableState["pagination"]["totalItems"], - }>) { - state.multiSelect = action.payload.multiSelect; - state.columns = action.payload.columns; - state.resource = action.payload.resource; - state.pages = action.payload.pages; - state.rows = action.payload.rows; - state.sortBy[action.payload.resource] = action.payload.sortBy; - state.reverse[action.payload.resource] = action.payload.reverse; - state.pagination = { - ...state.pagination, - totalItems: action.payload.totalItems, - }; - }, - loadColumns(state, action: PayloadAction< - TableState["columns"] - >) { - state.columns = action.payload; - }, - selectRow(state, action: PayloadAction< - number | string - >) { - const id = action.payload; - state.rows = state.rows.map((row) => { - if ("id" in row && row.id === id) { - return { - ...row, - selected: !row.selected, - }; - } - return row; - }) - }, - selectAll(state) { - state.rows = state.rows.map((row) => { - return { - ...row, - selected: true, - }; - }) - }, - deselectAll(state) { - state.rows = state.rows.map((row) => { - return { - ...row, - selected: false, - }; - }) - }, - reverseTable(state, action: PayloadAction< - TableState["reverse"][0] - >) { - state.reverse[state.resource] = action.payload; - }, - setSortBy(state, action: PayloadAction< - TableState["sortBy"][0] - >) { - state.sortBy[state.resource] = action.payload; - }, - createPage(state, action: PayloadAction< - Page - >) { - state.pages = state.pages.concat(action.payload) - }, - updatePageSize(state, action: PayloadAction< - TableState["pagination"]["limit"] - >) { - state.pagination = { - ...state.pagination, - limit: action.payload, - } - }, - setPages(state, action: PayloadAction< - TableState["pages"] - >) { - state.pages = action.payload; - }, - setTotalItems(state, action: PayloadAction< - TableState["pagination"]["totalItems"] - >) { - state.pagination = { - ...state.pagination, - totalItems: action.payload, - } - }, - setOffset(state, action: PayloadAction< - TableState["pagination"]["offset"] - >) { - state.pagination = { - ...state.pagination, - offset: action.payload, - } - }, - setDirectAccessiblePages(state, action: PayloadAction< - TableState["pagination"]["directAccessibleNo"] - >) { - state.pagination = { - ...state.pagination, - directAccessibleNo: action.payload, - } - }, - setPageActive(state, action: PayloadAction< - number - >) { - const pageNumber = action.payload; - state.pages = state.pages.map((page) => { - if (page.number === pageNumber) { - return { - ...page, - active: true, - }; - } else { - return { - ...page, - active: false, - }; - } - }) - }, - }, + name: 'table', + initialState, + reducers: { + loadResourceIntoTable(state, action: PayloadAction<{ + multiSelect: TableState["multiSelect"], + columns: TableConfig["columns"], + resource: TableState["resource"], + pages: TableState["pages"], + rows: TableState["rows"], + sortBy: TableState["sortBy"][0], + reverse: TableState["reverse"][0], + totalItems: TableState["pagination"]["totalItems"], + }>) { + state.multiSelect = action.payload.multiSelect; + state.columns = action.payload.columns; + state.resource = action.payload.resource; + state.pages = action.payload.pages; + state.rows = action.payload.rows; + state.sortBy[action.payload.resource] = action.payload.sortBy; + state.reverse[action.payload.resource] = action.payload.reverse; + state.pagination = { + ...state.pagination, + totalItems: action.payload.totalItems, + }; + }, + loadColumns(state, action: PayloadAction< + TableState["columns"] + >) { + state.columns = action.payload; + }, + selectRow(state, action: PayloadAction< + number | string + >) { + const id = action.payload; + state.rows = state.rows.map((row) => { + if ("id" in row && row.id === id) { + return { + ...row, + selected: !row.selected, + }; + } + return row; + }) + }, + selectAll(state) { + state.rows = state.rows.map((row) => { + return { + ...row, + selected: true, + }; + }) + }, + deselectAll(state) { + state.rows = state.rows.map((row) => { + return { + ...row, + selected: false, + }; + }) + }, + reverseTable(state, action: PayloadAction< + TableState["reverse"][0] + >) { + state.reverse[state.resource] = action.payload; + }, + setSortBy(state, action: PayloadAction< + TableState["sortBy"][0] + >) { + state.sortBy[state.resource] = action.payload; + }, + createPage(state, action: PayloadAction< + Page + >) { + state.pages = state.pages.concat(action.payload) + }, + updatePageSize(state, action: PayloadAction< + TableState["pagination"]["limit"] + >) { + state.pagination = { + ...state.pagination, + limit: action.payload, + } + }, + setPages(state, action: PayloadAction< + TableState["pages"] + >) { + state.pages = action.payload; + }, + setTotalItems(state, action: PayloadAction< + TableState["pagination"]["totalItems"] + >) { + state.pagination = { + ...state.pagination, + totalItems: action.payload, + } + }, + setOffset(state, action: PayloadAction< + TableState["pagination"]["offset"] + >) { + state.pagination = { + ...state.pagination, + offset: action.payload, + } + }, + setDirectAccessiblePages(state, action: PayloadAction< + TableState["pagination"]["directAccessibleNo"] + >) { + state.pagination = { + ...state.pagination, + directAccessibleNo: action.payload, + } + }, + setPageActive(state, action: PayloadAction< + number + >) { + const pageNumber = action.payload; + state.pages = state.pages.map((page) => { + if (page.number === pageNumber) { + return { + ...page, + active: true, + }; + } else { + return { + ...page, + active: false, + }; + } + }) + }, + }, }); export const { - loadResourceIntoTable, - loadColumns, - selectRow, - selectAll, - deselectAll, - reverseTable, - setSortBy, - createPage, - updatePageSize, - setPages, - setTotalItems, - setOffset, - setDirectAccessiblePages, - setPageActive + loadResourceIntoTable, + loadColumns, + selectRow, + selectAll, + deselectAll, + reverseTable, + setSortBy, + createPage, + updatePageSize, + setPages, + setTotalItems, + setOffset, + setDirectAccessiblePages, + setPageActive } = tableSlice.actions; // Export the slice reducer as the default export diff --git a/src/slices/themeDetailsSlice.ts b/src/slices/themeDetailsSlice.ts index 7bded2f6a6..1f691f3e7d 100644 --- a/src/slices/themeDetailsSlice.ts +++ b/src/slices/themeDetailsSlice.ts @@ -9,145 +9,145 @@ import { ThemeDetailsInitialValues, ThemeDetailsType } from './themeSlice'; * This file contains redux reducer for actions affecting the state of a theme */ export type Usage = { - series: {id: string, title: string}[] + series: {id: string, title: string}[] } type ThemeDetailsState = { - status: 'uninitialized' | 'loading' | 'succeeded' | 'failed', - error: SerializedError | null, - statusUsage: 'uninitialized' | 'loading' | 'succeeded' | 'failed', - errorUsage: SerializedError | null, - details: ThemeDetailsType, - usage: Usage, + status: 'uninitialized' | 'loading' | 'succeeded' | 'failed', + error: SerializedError | null, + statusUsage: 'uninitialized' | 'loading' | 'succeeded' | 'failed', + errorUsage: SerializedError | null, + details: ThemeDetailsType, + usage: Usage, }; // Initial state of theme details in redux store const initialState: ThemeDetailsState = { - status: 'uninitialized', - error: null, - statusUsage: 'uninitialized', - errorUsage: null, - details: { - bumperActive: false, - bumperFile: "", - creationDate: "", - creator: "", - default: false, - description: "", - id: 0, - licenseSlideActive: false, - licenseSlideBackground: "", - licenseSlideDescription: "", - name: "", - titleSlideActive: false, - titleSlideBackground: "", - titleSlideMetadata: "", - trailerActive: false, - trailerFile: "", - watermarkActive: false, - watermarkFile: "", - watermarkPosition: "", - }, - usage: { series: [] }, + status: 'uninitialized', + error: null, + statusUsage: 'uninitialized', + errorUsage: null, + details: { + bumperActive: false, + bumperFile: "", + creationDate: "", + creator: "", + default: false, + description: "", + id: 0, + licenseSlideActive: false, + licenseSlideBackground: "", + licenseSlideDescription: "", + name: "", + titleSlideActive: false, + titleSlideBackground: "", + titleSlideMetadata: "", + trailerActive: false, + trailerFile: "", + watermarkActive: false, + watermarkFile: "", + watermarkPosition: "", + }, + usage: { series: [] }, }; // fetch details of certain theme from server export const fetchThemeDetails = createAppAsyncThunk('themeDetails/fetchThemeDetails', async (id: ThemeDetailsState["details"]["id"]) => { - // Just make the async request here, and return the response. - // This will automatically dispatch a `pending` action first, - // and then `fulfilled` or `rejected` actions based on the promise. - const res = await axios.get(`/admin-ng/themes/${id}.json`); - return res.data; + // Just make the async request here, and return the response. + // This will automatically dispatch a `pending` action first, + // and then `fulfilled` or `rejected` actions based on the promise. + const res = await axios.get(`/admin-ng/themes/${id}.json`); + return res.data; }); // fetch usage of a certain theme export const fetchUsage = createAppAsyncThunk('themeDetails/fetchUsage', async (id: ThemeDetailsState["details"]["id"]) => { - const res = await axios.get(`/admin-ng/themes/${id}/usage.json`); - return res.data; + const res = await axios.get(`/admin-ng/themes/${id}/usage.json`); + return res.data; }); // update a certain theme export const updateThemeDetails = createAppAsyncThunk('themeDetails/updateThemeDetails', async (params: { - id: ThemeDetailsState["details"]["id"], - values: ThemeDetailsInitialValues + id: ThemeDetailsState["details"]["id"], + values: ThemeDetailsInitialValues }, {dispatch}) => { - const { values, id } = params - let data = buildThemeBody(values); + const { values, id } = params + let data = buildThemeBody(values); - // request for updating - axios - .put(`/admin-ng/themes/${id}`, data, { - headers: { - "Content-Type": "application/x-www-form-urlencoded", - }, - }) - .then((response) => { - console.info(response); - dispatch(addNotification({type: "success", key: "THEME_CREATED"})); - }) - .catch((response) => { - console.error(response); - dispatch(addNotification({type: "error", key: "THEME_NOT_CREATED"})); - }); + // request for updating + axios + .put(`/admin-ng/themes/${id}`, data, { + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + }) + .then((response) => { + console.info(response); + dispatch(addNotification({type: "success", key: "THEME_CREATED"})); + }) + .catch((response) => { + console.error(response); + dispatch(addNotification({type: "error", key: "THEME_NOT_CREATED"})); + }); }); const themeDetailsSlice = createSlice({ - name: 'themeDetails', - initialState, - reducers: {}, - // These are used for thunks - extraReducers: builder => { - builder - .addCase(fetchThemeDetails.pending, (state) => { - state.status = 'loading'; - }) - .addCase(fetchThemeDetails.fulfilled, (state, action: PayloadAction< - ThemeDetailsState["details"] - >) => { - state.status = 'succeeded'; - const themeDetails = action.payload; - state.details = themeDetails; - }) - .addCase(fetchThemeDetails.rejected, (state, action) => { - state.status = 'failed'; - state.details = { - bumperActive: false, - bumperFile: "", - creationDate: "", - creator: "", - default: false, - description: "", - id: 0, - licenseSlideActive: false, - licenseSlideBackground: "", - licenseSlideDescription: "", - name: "", - titleSlideActive: false, - titleSlideBackground: "", - titleSlideMetadata: "", - trailerActive: false, - trailerFile: "", - watermarkActive: false, - watermarkFile: "", - watermarkPosition: "", - }; - state.error = action.error; - }) - .addCase(fetchUsage.pending, (state) => { - state.status = 'loading'; - }) - .addCase(fetchUsage.fulfilled, (state, action: PayloadAction< - ThemeDetailsState["usage"] - >) => { - state.status = 'succeeded'; - const usage = action.payload; - state.usage = usage; - }) - .addCase(fetchUsage.rejected, (state, action) => { - state.status = 'failed'; - state.error = action.error; - }); - } + name: 'themeDetails', + initialState, + reducers: {}, + // These are used for thunks + extraReducers: builder => { + builder + .addCase(fetchThemeDetails.pending, (state) => { + state.status = 'loading'; + }) + .addCase(fetchThemeDetails.fulfilled, (state, action: PayloadAction< + ThemeDetailsState["details"] + >) => { + state.status = 'succeeded'; + const themeDetails = action.payload; + state.details = themeDetails; + }) + .addCase(fetchThemeDetails.rejected, (state, action) => { + state.status = 'failed'; + state.details = { + bumperActive: false, + bumperFile: "", + creationDate: "", + creator: "", + default: false, + description: "", + id: 0, + licenseSlideActive: false, + licenseSlideBackground: "", + licenseSlideDescription: "", + name: "", + titleSlideActive: false, + titleSlideBackground: "", + titleSlideMetadata: "", + trailerActive: false, + trailerFile: "", + watermarkActive: false, + watermarkFile: "", + watermarkPosition: "", + }; + state.error = action.error; + }) + .addCase(fetchUsage.pending, (state) => { + state.status = 'loading'; + }) + .addCase(fetchUsage.fulfilled, (state, action: PayloadAction< + ThemeDetailsState["usage"] + >) => { + state.status = 'succeeded'; + const usage = action.payload; + state.usage = usage; + }) + .addCase(fetchUsage.rejected, (state, action) => { + state.status = 'failed'; + state.error = action.error; + }); + } }); // export const {} = aclsSlice.actions; diff --git a/src/slices/themeSlice.ts b/src/slices/themeSlice.ts index 6aec533c78..145bdf3999 100644 --- a/src/slices/themeSlice.ts +++ b/src/slices/themeSlice.ts @@ -10,166 +10,166 @@ import { createAppAsyncThunk } from '../createAsyncThunkWithTypes'; * This file contains redux reducer for actions affecting the state of themes */ export type ThemeDetailsType = { - bumperActive: boolean, - bumperFile: string, - creationDate: string, - creator: string, - default: boolean, - description: string, - id: number, - licenseSlideActive: boolean, - licenseSlideBackground: string, - licenseSlideDescription: string, - name: string, - titleSlideActive: boolean, - titleSlideBackground: string, - titleSlideMetadata: string, - trailerActive: boolean, - trailerFile: string, - watermarkActive: boolean, - watermarkFile: string, - watermarkPosition: string, + bumperActive: boolean, + bumperFile: string, + creationDate: string, + creator: string, + default: boolean, + description: string, + id: number, + licenseSlideActive: boolean, + licenseSlideBackground: string, + licenseSlideDescription: string, + name: string, + titleSlideActive: boolean, + titleSlideBackground: string, + titleSlideMetadata: string, + trailerActive: boolean, + trailerFile: string, + watermarkActive: boolean, + watermarkFile: string, + watermarkPosition: string, } export type ThemeDetailsInitialValues = ThemeDetailsType & { titleSlideMode: string } type ThemeState = { - status: 'uninitialized' | 'loading' | 'succeeded' | 'failed', - error: SerializedError | null, - results: ThemeDetailsType[], - columns: TableConfig["columns"], - total: number, - count: number, - offset: number, - limit: number, + status: 'uninitialized' | 'loading' | 'succeeded' | 'failed', + error: SerializedError | null, + results: ThemeDetailsType[], + columns: TableConfig["columns"], + total: number, + count: number, + offset: number, + limit: number, }; // Fill columns initially with columns defined in themesTableConfig const initialColumns = themesTableConfig.columns.map((column) => ({ - ...column, - deactivated: false, + ...column, + deactivated: false, })); // Initial state of themes in redux store const initialState: ThemeState = { - status: 'uninitialized', - error: null, - results: [], - columns: initialColumns, - total: 0, - count: 0, - offset: 0, - limit: 0, + status: 'uninitialized', + error: null, + results: [], + columns: initialColumns, + total: 0, + count: 0, + offset: 0, + limit: 0, }; // fetch themes from server export const fetchThemes = createAppAsyncThunk('theme/fetchThemes', async (_, { getState }) => { - const state = getState(); - let params = getURLParams(state, "themes"); - // Just make the async request here, and return the response. - // This will automatically dispatch a `pending` action first, - // and then `fulfilled` or `rejected` actions based on the promise. + const state = getState(); + let params = getURLParams(state, "themes"); + // Just make the async request here, and return the response. + // This will automatically dispatch a `pending` action first, + // and then `fulfilled` or `rejected` actions based on the promise. // /themes.json?limit=0&offset=0&filter={filter}&sort={sort} - const res = await axios.get("/admin-ng/themes/themes.json", { params: params }); - return res.data; + const res = await axios.get("/admin-ng/themes/themes.json", { params: params }); + return res.data; }); // post new theme to backend export const postNewTheme = createAppAsyncThunk('theme/postNewTheme', async (values: ThemeDetailsInitialValues - // All params that would be accepted by the endpoint - // { - // default: boolean, - // name: string, - // description: string - // bumperActive: boolean, - // trailerActive: boolean, - // titleSlideActive: boolean, - // licenseSlideActive: boolean, - // watermarkActive: boolean, - // bumperFile: string, - // trailerFile: string, - // watermarkFile: string, - // titleSlideBackground: string, - // licenseSlideBackground: string, - // titleSlideMetadata: string, - // licenseSlideDescription: string, - // watermarkPosition: string, + // All params that would be accepted by the endpoint + // { + // default: boolean, + // name: string, + // description: string + // bumperActive: boolean, + // trailerActive: boolean, + // titleSlideActive: boolean, + // licenseSlideActive: boolean, + // watermarkActive: boolean, + // bumperFile: string, + // trailerFile: string, + // watermarkFile: string, + // titleSlideBackground: string, + // licenseSlideBackground: string, + // titleSlideMetadata: string, + // licenseSlideDescription: string, + // watermarkPosition: string, // } , {dispatch}) => { - // get URL params used for post request - let data = buildThemeBody(values); + // get URL params used for post request + let data = buildThemeBody(values); - axios - .post("/admin-ng/themes", data, { - headers: { - "Content-Type": "application/x-www-form-urlencoded", - }, - }) - // Usually we would extraReducers for responses, but reducers are not allowed to dispatch - // (they need to be free of side effects) - // Since we want to dispatch, we have to handle responses in our thunk - .then((response) => { - console.info(response); - dispatch(addNotification({type: "success", key: "THEME_CREATED"})); - }) - .catch((response) => { - console.error(response); - dispatch(addNotification({type: "error", key: "THEME_NOT_CREATED"})); - }); + axios + .post("/admin-ng/themes", data, { + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + }) + // Usually we would extraReducers for responses, but reducers are not allowed to dispatch + // (they need to be free of side effects) + // Since we want to dispatch, we have to handle responses in our thunk + .then((response) => { + console.info(response); + dispatch(addNotification({type: "success", key: "THEME_CREATED"})); + }) + .catch((response) => { + console.error(response); + dispatch(addNotification({type: "error", key: "THEME_NOT_CREATED"})); + }); }); // delete theme with provided id export const deleteTheme = createAppAsyncThunk('theme/deleteTheme', async (id: ThemeDetailsType["id"], {dispatch}) => { - axios - .delete(`/admin-ng/themes/${id}`) - .then((res) => { - console.info(res); - // add success notification - dispatch(addNotification({type: "success", key: "THEME_DELETED"})); - }) - .catch((res) => { - console.error(res); - // add error notification - dispatch(addNotification({type: "error", key: "THEME_NOT_DELETED"})); - }); + axios + .delete(`/admin-ng/themes/${id}`) + .then((res) => { + console.info(res); + // add success notification + dispatch(addNotification({type: "success", key: "THEME_DELETED"})); + }) + .catch((res) => { + console.error(res); + // add error notification + dispatch(addNotification({type: "error", key: "THEME_NOT_DELETED"})); + }); }); const themeSlice = createSlice({ - name: 'theme', - initialState, - reducers: { - setThemeColumns(state, action: PayloadAction< - ThemeState["columns"] - >) { - state.columns = action.payload; - }, - }, - // These are used for thunks - extraReducers: builder => { - builder - .addCase(fetchThemes.pending, (state) => { - state.status = 'loading'; - }) - .addCase(fetchThemes.fulfilled, (state, action: PayloadAction<{ - total: ThemeState["total"], - count: ThemeState["count"], - limit: ThemeState["limit"], - offset: ThemeState["offset"], - results: ThemeState["results"], - }>) => { - state.status = 'succeeded'; - const acls = action.payload; - state.total = acls.total; - state.count = acls.count; - state.limit = acls.limit; - state.offset = acls.offset; - state.results = acls.results; - }) - .addCase(fetchThemes.rejected, (state, action) => { - state.status = 'failed'; - state.error = action.error; - }); - } + name: 'theme', + initialState, + reducers: { + setThemeColumns(state, action: PayloadAction< + ThemeState["columns"] + >) { + state.columns = action.payload; + }, + }, + // These are used for thunks + extraReducers: builder => { + builder + .addCase(fetchThemes.pending, (state) => { + state.status = 'loading'; + }) + .addCase(fetchThemes.fulfilled, (state, action: PayloadAction<{ + total: ThemeState["total"], + count: ThemeState["count"], + limit: ThemeState["limit"], + offset: ThemeState["offset"], + results: ThemeState["results"], + }>) => { + state.status = 'succeeded'; + const acls = action.payload; + state.total = acls.total; + state.count = acls.count; + state.limit = acls.limit; + state.offset = acls.offset; + state.results = acls.results; + }) + .addCase(fetchThemes.rejected, (state, action) => { + state.status = 'failed'; + state.error = action.error; + }); + } }); export const { setThemeColumns } = themeSlice.actions; diff --git a/src/slices/userDetailsSlice.ts b/src/slices/userDetailsSlice.ts index b823f5b379..3661e297f5 100644 --- a/src/slices/userDetailsSlice.ts +++ b/src/slices/userDetailsSlice.ts @@ -9,106 +9,106 @@ import { UserRole } from './userSlice'; * This file contains redux reducer for actions affecting the state of details of a user */ export type UpdateUser = { - email?: string, - name?: string, - password?: string, - roles?: UserRole[], - username: string, + email?: string, + name?: string, + password?: string, + roles?: UserRole[], + username: string, } export type UserDetailsState = { - status: 'uninitialized' | 'loading' | 'succeeded' | 'failed', - error: SerializedError | null, - provider: string, - roles: UserRole[], - name: string, - username: string, - email: string, - manageable: boolean, + status: 'uninitialized' | 'loading' | 'succeeded' | 'failed', + error: SerializedError | null, + provider: string, + roles: UserRole[], + name: string, + username: string, + email: string, + manageable: boolean, }; // Initial state of userDetails in redux store const initialState: UserDetailsState = { - status: 'uninitialized', - error: null, - provider: "", - roles: [], - name: "", - username: "", - email: "", - manageable: false, + status: 'uninitialized', + error: null, + provider: "", + roles: [], + name: "", + username: "", + email: "", + manageable: false, }; // fetch details about certain user from server export const fetchUserDetails = createAppAsyncThunk('userDetails/fetchUserDetails', async (username: UserDetailsState["name"]) => { - // Just make the async request here, and return the response. - // This will automatically dispatch a `pending` action first, - // and then `fulfilled` or `rejected` actions based on the promise. - const res = await axios.get(`/admin-ng/users/${username}.json`); - return res.data; + // Just make the async request here, and return the response. + // This will automatically dispatch a `pending` action first, + // and then `fulfilled` or `rejected` actions based on the promise. + const res = await axios.get(`/admin-ng/users/${username}.json`); + return res.data; }); // update existing user with changed values export const updateUserDetails = createAppAsyncThunk('userDetails/updateUserDetails', async (params: { - values: UpdateUser, - username: UserDetailsState["name"] + values: UpdateUser, + username: UserDetailsState["name"] }, {dispatch}) => { - const { username, values } = params + const { username, values } = params - // get URL params used for put request - let data = buildUserBody(values); + // get URL params used for put request + let data = buildUserBody(values); - // PUT request - axios - .put(`/admin-ng/users/${username}.json`, data) - .then((response) => { - console.info(response); - dispatch(addNotification({type: "success", key: "USER_UPDATED"})); - }) - .catch((response) => { - console.error(response); - dispatch(addNotification({type: "error", key: "USER_NOT_SAVED"})); - }); + // PUT request + axios + .put(`/admin-ng/users/${username}.json`, data) + .then((response) => { + console.info(response); + dispatch(addNotification({type: "success", key: "USER_UPDATED"})); + }) + .catch((response) => { + console.error(response); + dispatch(addNotification({type: "error", key: "USER_NOT_SAVED"})); + }); }); const userDetailsSlice = createSlice({ - name: 'userDetails', - initialState, - reducers: {}, - // These are used for thunks - extraReducers: builder => { - builder - .addCase(fetchUserDetails.pending, (state) => { - state.status = 'loading'; - }) - .addCase(fetchUserDetails.fulfilled, (state, action: PayloadAction<{ - provider: UserDetailsState["provider"], - roles: UserDetailsState["roles"], - name: UserDetailsState["name"], - username: UserDetailsState["username"], - email: UserDetailsState["email"], - manageable: UserDetailsState["manageable"], - }>) => { - state.status = 'succeeded'; - const userDetails = action.payload; - state.provider = userDetails.provider; - state.roles = userDetails.roles; - state.name = userDetails.name; - state.username = userDetails.username; - state.email = !!userDetails.email ? userDetails.email : ""; - state.manageable = userDetails.manageable; - }) - .addCase(fetchUserDetails.rejected, (state, action) => { - state.status = 'failed'; - state.error = action.error; - state.provider = ""; - state.roles = []; - state.name = ""; - state.username = ""; - state.email = ""; - state.manageable = false; - }); - } + name: 'userDetails', + initialState, + reducers: {}, + // These are used for thunks + extraReducers: builder => { + builder + .addCase(fetchUserDetails.pending, (state) => { + state.status = 'loading'; + }) + .addCase(fetchUserDetails.fulfilled, (state, action: PayloadAction<{ + provider: UserDetailsState["provider"], + roles: UserDetailsState["roles"], + name: UserDetailsState["name"], + username: UserDetailsState["username"], + email: UserDetailsState["email"], + manageable: UserDetailsState["manageable"], + }>) => { + state.status = 'succeeded'; + const userDetails = action.payload; + state.provider = userDetails.provider; + state.roles = userDetails.roles; + state.name = userDetails.name; + state.username = userDetails.username; + state.email = !!userDetails.email ? userDetails.email : ""; + state.manageable = userDetails.manageable; + }) + .addCase(fetchUserDetails.rejected, (state, action) => { + state.status = 'failed'; + state.error = action.error; + state.provider = ""; + state.roles = []; + state.name = ""; + state.username = ""; + state.email = ""; + state.manageable = false; + }); + } }); // export const {} = userDetailsSlice.actions; diff --git a/src/slices/userInfoSlice.ts b/src/slices/userInfoSlice.ts index ff6b0cf827..81e929e16a 100644 --- a/src/slices/userInfoSlice.ts +++ b/src/slices/userInfoSlice.ts @@ -7,158 +7,158 @@ import { createAppAsyncThunk } from '../createAsyncThunkWithTypes'; * This file contains redux reducer for actions affecting the state of information about current user */ type OcVersion = { - buildNumber: string | undefined, - consistent: boolean | undefined, - 'last-modified': number | undefined, - version: string | undefined, + buildNumber: string | undefined, + consistent: boolean | undefined, + 'last-modified': number | undefined, + version: string | undefined, } type UserInfoOrganization = { - adminRole: string, - anonymousRole: string, - id: string, - name: string, - properties: { [key: string]: string }, + adminRole: string, + anonymousRole: string, + id: string, + name: string, + properties: { [key: string]: string }, } type UserInfoUser = { - email: string, - name: string, - provider: string, - username: string, + email: string, + name: string, + provider: string, + username: string, } export type UserInfoState = { - status: 'uninitialized' | 'loading' | 'succeeded' | 'failed', - error: SerializedError | null, - statusOcVersion: 'uninitialized' | 'loading' | 'succeeded' | 'failed', - errorOcVersion: SerializedError | null, - isAdmin: boolean, - isOrgAdmin: boolean, - org: UserInfoOrganization, - roles: string[], - userRole: string, - user: UserInfoUser, - ocVersion: OcVersion, + status: 'uninitialized' | 'loading' | 'succeeded' | 'failed', + error: SerializedError | null, + statusOcVersion: 'uninitialized' | 'loading' | 'succeeded' | 'failed', + errorOcVersion: SerializedError | null, + isAdmin: boolean, + isOrgAdmin: boolean, + org: UserInfoOrganization, + roles: string[], + userRole: string, + user: UserInfoUser, + ocVersion: OcVersion, }; // Initial state of userInfo in redux store const initialState: UserInfoState = { - status: 'uninitialized', - error: null, - statusOcVersion: 'uninitialized', - errorOcVersion: null, - isAdmin: false, - isOrgAdmin: false, - org: { - adminRole: "", - anonymousRole: "", - id: "", - name: "", - properties: {} - }, - roles: [], - userRole: "", - user: { - email: "", - name: "", - provider: "", - username: "", - }, - ocVersion: { - buildNumber: undefined, - consistent: undefined, - "last-modified": undefined, - version: undefined, - }, + status: 'uninitialized', + error: null, + statusOcVersion: 'uninitialized', + errorOcVersion: null, + isAdmin: false, + isOrgAdmin: false, + org: { + adminRole: "", + anonymousRole: "", + id: "", + name: "", + properties: {} + }, + roles: [], + userRole: "", + user: { + email: "", + name: "", + provider: "", + username: "", + }, + ocVersion: { + buildNumber: undefined, + consistent: undefined, + "last-modified": undefined, + version: undefined, + }, }; export const fetchUserInfo = createAppAsyncThunk('UserInfo/fetchUserInfo', async (_, { dispatch }) => { - // Just make the async request here, and return the response. - // This will automatically dispatch a `pending` action first, - // and then `fulfilled` or `rejected` actions based on the promise. - const res = await axios.get("/info/me.json") - .then((response) => { - return response.data; - }) - .catch((response) => { - console.error(response); - dispatch(addNotification({type: "error", key: "USER_NOT_SAVED"})); - }); + // Just make the async request here, and return the response. + // This will automatically dispatch a `pending` action first, + // and then `fulfilled` or `rejected` actions based on the promise. + const res = await axios.get("/info/me.json") + .then((response) => { + return response.data; + }) + .catch((response) => { + console.error(response); + dispatch(addNotification({type: "error", key: "USER_NOT_SAVED"})); + }); - // Redirect to login if not in ROLE_ADMIN_UI - if (!(res.roles.includes('ROLE_ADMIN') || res.roles.includes('ROLE_ADMIN_UI'))) { - window.location.href = "/login.html"; - } + // Redirect to login if not in ROLE_ADMIN_UI + if (!(res.roles.includes('ROLE_ADMIN') || res.roles.includes('ROLE_ADMIN_UI'))) { + window.location.href = "/login.html"; + } - return res; + return res; }); export const fetchOcVersion = createAppAsyncThunk('UserInfo/fetchOcVersion', async () => { - const res = await axios.get("/sysinfo/bundles/version?prefix=opencast"); - return res.data; + const res = await axios.get("/sysinfo/bundles/version?prefix=opencast"); + return res.data; }); const userInfoSlice = createSlice({ - name: 'userInfo', - initialState, - reducers: {}, - // These are used for thunks - extraReducers: builder => { - builder - .addCase(fetchUserInfo.pending, (state) => { - state.status = 'loading'; - }) - .addCase(fetchUserInfo.fulfilled, (state, action: PayloadAction<{ - org: UserInfoState["org"], - roles: UserInfoState["roles"], - userRole: UserInfoState["userRole"], - user: UserInfoState["user"], - }>) => { - state.status = 'succeeded'; - const userInfo = action.payload; - state.isAdmin = userInfo.roles.includes("ROLE_ADMIN"); - state.isOrgAdmin = userInfo.roles.includes(userInfo.org.adminRole); - state.org = userInfo.org; - state.roles = userInfo.roles; - state.userRole = userInfo.userRole; - state.user = userInfo.user; - }) - .addCase(fetchUserInfo.rejected, (state, action) => { - state.status = 'failed'; - state.org = { - adminRole: "", - anonymousRole: "", - id: "", - name: "", - properties: {} - }; - state.roles = []; - state.userRole = ""; - state.user = { - email: "", - name: "", - provider: "", - username: "", - }; - state.error = action.error; - }) - .addCase(fetchOcVersion.pending, (state) => { - state.statusOcVersion = 'loading'; - }) - .addCase(fetchOcVersion.fulfilled, (state, action: PayloadAction< + name: 'userInfo', + initialState, + reducers: {}, + // These are used for thunks + extraReducers: builder => { + builder + .addCase(fetchUserInfo.pending, (state) => { + state.status = 'loading'; + }) + .addCase(fetchUserInfo.fulfilled, (state, action: PayloadAction<{ + org: UserInfoState["org"], + roles: UserInfoState["roles"], + userRole: UserInfoState["userRole"], + user: UserInfoState["user"], + }>) => { + state.status = 'succeeded'; + const userInfo = action.payload; + state.isAdmin = userInfo.roles.includes("ROLE_ADMIN"); + state.isOrgAdmin = userInfo.roles.includes(userInfo.org.adminRole); + state.org = userInfo.org; + state.roles = userInfo.roles; + state.userRole = userInfo.userRole; + state.user = userInfo.user; + }) + .addCase(fetchUserInfo.rejected, (state, action) => { + state.status = 'failed'; + state.org = { + adminRole: "", + anonymousRole: "", + id: "", + name: "", + properties: {} + }; + state.roles = []; + state.userRole = ""; + state.user = { + email: "", + name: "", + provider: "", + username: "", + }; + state.error = action.error; + }) + .addCase(fetchOcVersion.pending, (state) => { + state.statusOcVersion = 'loading'; + }) + .addCase(fetchOcVersion.fulfilled, (state, action: PayloadAction< OcVersion - >) => { - state.statusOcVersion = 'succeeded'; - const ocVersion = action.payload; - state.ocVersion = ocVersion; - }) - .addCase(fetchOcVersion.rejected, (state, action) => { - state.statusOcVersion = 'failed'; - state.errorOcVersion = action.error; - }); - } + >) => { + state.statusOcVersion = 'succeeded'; + const ocVersion = action.payload; + state.ocVersion = ocVersion; + }) + .addCase(fetchOcVersion.rejected, (state, action) => { + state.statusOcVersion = 'failed'; + state.errorOcVersion = action.error; + }); + } }); diff --git a/src/slices/userSlice.ts b/src/slices/userSlice.ts index 7d3f9b0997..4b3f7221a0 100644 --- a/src/slices/userSlice.ts +++ b/src/slices/userSlice.ts @@ -11,157 +11,157 @@ import { createAppAsyncThunk } from '../createAsyncThunkWithTypes'; * This file contains redux reducer for actions affecting the state of users */ export type UserRole = { - name: string - type: string + name: string + type: string } export type User = { - email?: string, - manageable: boolean, - name: string, - provider: string, - roles: UserRole[], - username: string, + email?: string, + manageable: boolean, + name: string, + provider: string, + roles: UserRole[], + username: string, } export type NewUser = { - email?: string, - name?: string, - password: string, - roles?: UserRole[], - username: string, + email?: string, + name?: string, + password: string, + roles?: UserRole[], + username: string, } type UsersState = { - status: 'uninitialized' | 'loading' | 'succeeded' | 'failed', - error: SerializedError | null, - results: User[], - columns: TableConfig["columns"], - total: number, - count: number, - offset: number, - limit: number, + status: 'uninitialized' | 'loading' | 'succeeded' | 'failed', + error: SerializedError | null, + results: User[], + columns: TableConfig["columns"], + total: number, + count: number, + offset: number, + limit: number, }; // Fill columns initially with columns defined in usersTableConfig const initialColumns = usersTableConfig.columns.map((column) => ({ - ...column, - deactivated: false, + ...column, + deactivated: false, })); // Initial state of users in redux store const initialState: UsersState = { - status: 'uninitialized', + status: 'uninitialized', error: null, - results: [], - columns: initialColumns, - total: 0, - count: 0, - offset: 0, - limit: 0, + results: [], + columns: initialColumns, + total: 0, + count: 0, + offset: 0, + limit: 0, }; // fetch users from server export const fetchUsers = createAppAsyncThunk('users/fetchUsers', async (_, { getState }) => { - const state = getState(); - let params = getURLParams(state, "users"); - // Just make the async request here, and return the response. - // This will automatically dispatch a `pending` action first, - // and then `fulfilled` or `rejected` actions based on the promise. - const res = await axios.get("/admin-ng/users/users.json", { params: params }); - return res.data; + const state = getState(); + let params = getURLParams(state, "users"); + // Just make the async request here, and return the response. + // This will automatically dispatch a `pending` action first, + // and then `fulfilled` or `rejected` actions based on the promise. + const res = await axios.get("/admin-ng/users/users.json", { params: params }); + return res.data; }); // new user to backend export const postNewUser = createAppAsyncThunk('users/postNewUser', async (values: NewUser, {dispatch}) => { - // get URL params used for post request - let data = buildUserBody(values); - - axios - .post("/admin-ng/users", data, { - headers: { - "Content-Type": "application/x-www-form-urlencoded", - }, - }) - // Usually we would extraReducers for responses, but reducers are not allowed to dispatch - // (they need to be free of side effects) - // Since we want to dispatch, we have to handle responses in our thunk - .then((response) => { - console.info(response); - dispatch(addNotification({type: "success", key: "USER_ADDED"})); - }) - .catch((response) => { - console.error(response); - dispatch(addNotification({type: "error", key: "USER_NOT_SAVED"})); - }); + // get URL params used for post request + let data = buildUserBody(values); + + axios + .post("/admin-ng/users", data, { + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + }) + // Usually we would extraReducers for responses, but reducers are not allowed to dispatch + // (they need to be free of side effects) + // Since we want to dispatch, we have to handle responses in our thunk + .then((response) => { + console.info(response); + dispatch(addNotification({type: "success", key: "USER_ADDED"})); + }) + .catch((response) => { + console.error(response); + dispatch(addNotification({type: "error", key: "USER_NOT_SAVED"})); + }); }); // delete user with provided id export const deleteUser = createAppAsyncThunk('users/deleteUser', async (id: string, {dispatch}) => { - // API call for deleting an user - axios - .delete(`/admin-ng/users/${id}.json`) - .then((res) => { - console.info(res); - // add success notification - dispatch(addNotification({type: "success", key: "USER_DELETED"})); - }) - .catch((res) => { - console.error(res); - // add error notification - dispatch(addNotification({type: "error", key: "USER_NOT_DELETED"})); - }); + // API call for deleting an user + axios + .delete(`/admin-ng/users/${id}.json`) + .then((res) => { + console.info(res); + // add success notification + dispatch(addNotification({type: "success", key: "USER_DELETED"})); + }) + .catch((res) => { + console.error(res); + // add error notification + dispatch(addNotification({type: "error", key: "USER_NOT_DELETED"})); + }); }); // get users and their user names export const fetchUsersAndUsernames = async () => { - let data = await axios.get( - "/admin-ng/resources/USERS.NAME.AND.USERNAME.json" - ); + let data = await axios.get( + "/admin-ng/resources/USERS.NAME.AND.USERNAME.json" + ); - const response = await data.data; + const response = await data.data; - return transformToIdValueArray(response); + return transformToIdValueArray(response); }; const usersSlice = createSlice({ - name: 'users', - initialState, - reducers: { - setUserColumns(state, action: PayloadAction< - UsersState["columns"] - >) { - state.columns = action.payload; - }, - }, - // These are used for thunks - extraReducers: builder => { - builder - // fetchUsers - .addCase(fetchUsers.pending, (state) => { - state.status = 'loading'; - }) - .addCase(fetchUsers.fulfilled, (state, action: PayloadAction<{ - total: UsersState["total"], - count: UsersState["count"], - limit: UsersState["limit"], - offset: UsersState["offset"], - results: UsersState["results"], - }>) => { - state.status = 'succeeded'; - const users = action.payload; - state.total = users.total; - state.count = users.count; - state.limit = users.limit; - state.offset = users.offset; - state.results = users.results; - }) - .addCase(fetchUsers.rejected, (state, action) => { - state.status = 'failed'; - state.results = []; - state.error = action.error; - }); - } + name: 'users', + initialState, + reducers: { + setUserColumns(state, action: PayloadAction< + UsersState["columns"] + >) { + state.columns = action.payload; + }, + }, + // These are used for thunks + extraReducers: builder => { + builder + // fetchUsers + .addCase(fetchUsers.pending, (state) => { + state.status = 'loading'; + }) + .addCase(fetchUsers.fulfilled, (state, action: PayloadAction<{ + total: UsersState["total"], + count: UsersState["count"], + limit: UsersState["limit"], + offset: UsersState["offset"], + results: UsersState["results"], + }>) => { + state.status = 'succeeded'; + const users = action.payload; + state.total = users.total; + state.count = users.count; + state.limit = users.limit; + state.offset = users.offset; + state.results = users.results; + }) + .addCase(fetchUsers.rejected, (state, action) => { + state.status = 'failed'; + state.results = []; + state.error = action.error; + }); + } }); export const { setUserColumns } = usersSlice.actions; diff --git a/src/slices/workflowSlice.ts b/src/slices/workflowSlice.ts index a31ad3591b..d47c684fb7 100644 --- a/src/slices/workflowSlice.ts +++ b/src/slices/workflowSlice.ts @@ -6,130 +6,130 @@ import { createAppAsyncThunk } from '../createAsyncThunkWithTypes'; * This file contains redux reducer for actions affecting the state of workflows */ export type FieldSetField = { - name: string - value: unknown, - type: string, - checked: boolean, - fieldset?: FieldSetField[] - defaultValue?: unknown - [key: string]: unknown + name: string + value: unknown, + type: string, + checked: boolean, + fieldset?: FieldSetField[] + defaultValue?: unknown + [key: string]: unknown } type ConfigurationPanelField = { - // We could potentially specify 'fieldset' more, but I cannot find a definition - // for which key value pairs are allowed - fieldset?: FieldSetField[] // Values can be anything - legend?: string, - description?: string, + // We could potentially specify 'fieldset' more, but I cannot find a definition + // for which key value pairs are allowed + fieldset?: FieldSetField[] // Values can be anything + legend?: string, + description?: string, } export type Workflow = { - configuration_panel: string, //XML - configuration_panel_json: string | ConfigurationPanelField[], // 'string' will always be the empty string - description: string, - displayOrder: number, - id: string, - tags: string[], - title: string, + configuration_panel: string, //XML + configuration_panel_json: string | ConfigurationPanelField[], // 'string' will always be the empty string + description: string, + displayOrder: number, + id: string, + tags: string[], + title: string, } type WorkflowState = { - status: 'uninitialized' | 'loading' | 'succeeded' | 'failed', - error: SerializedError | null, - defaultWorkflowId: string, - workflows: Workflow[], + status: 'uninitialized' | 'loading' | 'succeeded' | 'failed', + error: SerializedError | null, + defaultWorkflowId: string, + workflows: Workflow[], }; // Initial state of workflows in redux store const initialState: WorkflowState = { - status: 'uninitialized', - error: null, - defaultWorkflowId: "", - workflows: [], + status: 'uninitialized', + error: null, + defaultWorkflowId: "", + workflows: [], }; // fetch workflow definitions from server export const fetchWorkflowDef = createAppAsyncThunk('workflow/fetchWorkflowDef', async (type: string) => { - let urlParams; + let urlParams; - switch (type) { - case "tasks": { - urlParams = { - tags: "archive", - }; - break; - } - case "delete-event": { - urlParams = { - tags: "delete", - }; - break; - } - case "event-details": - urlParams = { - tags: "schedule", - }; - break; - default: { - urlParams = { - tags: "upload,schedule", - }; - } - } + switch (type) { + case "tasks": { + urlParams = { + tags: "archive", + }; + break; + } + case "delete-event": { + urlParams = { + tags: "delete", + }; + break; + } + case "event-details": + urlParams = { + tags: "schedule", + }; + break; + default: { + urlParams = { + tags: "upload,schedule", + }; + } + } - // Just make the async request here, and return the response. - // This will automatically dispatch a `pending` action first, - // and then `fulfilled` or `rejected` actions based on the promise. - const res = await axios.get("/admin-ng/event/new/processing?", { params: urlParams }); - let workflows = res.data.workflows; + // Just make the async request here, and return the response. + // This will automatically dispatch a `pending` action first, + // and then `fulfilled` or `rejected` actions based on the promise. + const res = await axios.get("/admin-ng/event/new/processing?", { params: urlParams }); + let workflows = res.data.workflows; - workflows = workflows.map((workflow: Workflow) => { - if (workflow.configuration_panel_json.length > 0) { - return { - ...workflow, - configuration_panel_json: JSON.parse( - workflow.configuration_panel_json as string - ), - }; - } else { - return workflow; - } - }); + workflows = workflows.map((workflow: Workflow) => { + if (workflow.configuration_panel_json.length > 0) { + return { + ...workflow, + configuration_panel_json: JSON.parse( + workflow.configuration_panel_json as string + ), + }; + } else { + return workflow; + } + }); - const workflowDef = { - defaultWorkflowId: res.data.default_workflow_id, - workflows: workflows, - }; + const workflowDef = { + defaultWorkflowId: res.data.default_workflow_id, + workflows: workflows, + }; - return workflowDef; + return workflowDef; }); const workflowSlice = createSlice({ - name: 'workflow', - initialState, - reducers: {}, - // These are used for thunks - extraReducers: builder => { - builder - .addCase(fetchWorkflowDef.pending, (state) => { - state.status = 'loading'; - }) - // Pass the generated action creators to `.addCase()` - .addCase(fetchWorkflowDef.fulfilled, (state, action: PayloadAction<{ - defaultWorkflowId: WorkflowState["defaultWorkflowId"], - workflows: WorkflowState["workflows"], - }>) => { - // Same "mutating" update syntax thanks to Immer - state.status = 'succeeded'; - const acls = action.payload; - state.defaultWorkflowId = acls.defaultWorkflowId; - state.workflows = acls.workflows; - }) - .addCase(fetchWorkflowDef.rejected, (state, action) => { - state.status = 'failed'; - state.error = action.error; - }); - } + name: 'workflow', + initialState, + reducers: {}, + // These are used for thunks + extraReducers: builder => { + builder + .addCase(fetchWorkflowDef.pending, (state) => { + state.status = 'loading'; + }) + // Pass the generated action creators to `.addCase()` + .addCase(fetchWorkflowDef.fulfilled, (state, action: PayloadAction<{ + defaultWorkflowId: WorkflowState["defaultWorkflowId"], + workflows: WorkflowState["workflows"], + }>) => { + // Same "mutating" update syntax thanks to Immer + state.status = 'succeeded'; + const acls = action.payload; + state.defaultWorkflowId = acls.defaultWorkflowId; + state.workflows = acls.workflows; + }) + .addCase(fetchWorkflowDef.rejected, (state, action) => { + state.status = 'failed'; + state.error = action.error; + }); + } }); // export const {} = workflowSlice.actions; diff --git a/src/store.ts b/src/store.ts index d6973a7233..031bc51fb5 100644 --- a/src/store.ts +++ b/src/store.ts @@ -50,39 +50,39 @@ const themesPersistConfig = { key: "themes", storage, whitelist: ["columns"] } // form reducer and all other reducers used in this app const reducers = combineReducers({ - tableFilters, - tableFilterProfiles: persistReducer(tableFilterProfilesPersistConfig, tableFilterProfiles), - events: persistReducer(eventsPersistConfig, events), - series: persistReducer(seriesPersistConfig, series), - table: persistReducer(tablePersistConfig, table), - recordings: persistReducer(recordingsPersistConfig, recordings), - jobs: persistReducer(jobsPersistConfig, jobs), - servers: persistReducer(serversPersistConfig, servers), - services: persistReducer(servicesPersistConfig, services), - users: persistReducer(usersPersistConfig, users), - groups: persistReducer(groupsPersistConfig, groups), - acls: persistReducer(aclsPersistConfig, acls), - themes: persistReducer(themesPersistConfig, themes), - health, - notifications, - workflows, - eventDetails, - themeDetails, - seriesDetails, - recordingDetails, - userDetails, - groupDetails, - aclDetails, - userInfo, - statistics, + tableFilters, + tableFilterProfiles: persistReducer(tableFilterProfilesPersistConfig, tableFilterProfiles), + events: persistReducer(eventsPersistConfig, events), + series: persistReducer(seriesPersistConfig, series), + table: persistReducer(tablePersistConfig, table), + recordings: persistReducer(recordingsPersistConfig, recordings), + jobs: persistReducer(jobsPersistConfig, jobs), + servers: persistReducer(serversPersistConfig, servers), + services: persistReducer(servicesPersistConfig, services), + users: persistReducer(usersPersistConfig, users), + groups: persistReducer(groupsPersistConfig, groups), + acls: persistReducer(aclsPersistConfig, acls), + themes: persistReducer(themesPersistConfig, themes), + health, + notifications, + workflows, + eventDetails, + themeDetails, + seriesDetails, + recordingDetails, + userDetails, + groupDetails, + aclDetails, + userInfo, + statistics, }); // Configuration for persisting store const persistConfig = { - key: "root", - storage, - stateReconciler: autoMergeLevel2, - whitelist: ["tableFilters"], + key: "root", + storage, + stateReconciler: autoMergeLevel2, + whitelist: ["tableFilters"], }; const persistedReducer = persistReducer>(persistConfig, reducers); diff --git a/src/styles/base/_reset.scss b/src/styles/base/_reset.scss index e4520d3e0f..887a0a5f4e 100644 --- a/src/styles/base/_reset.scss +++ b/src/styles/base/_reset.scss @@ -15,43 +15,43 @@ article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video { - margin: 0; - padding: 0; - border: 0; - font: inherit; + margin: 0; + padding: 0; + border: 0; + font: inherit; font-size: 100%; - vertical-align: baseline; + vertical-align: baseline; } /* HTML5 display-role reset for older browsers */ article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section { - display: block; + display: block; } body { - line-height: 1; + line-height: 1; } ol, ul { - list-style: none; + list-style: none; } blockquote, q { - quotes: none; + quotes: none; } blockquote:before, blockquote:after, q:before, q:after { - content: ''; - content: none; + content: ''; + content: none; } table { - border-collapse: collapse; - border-spacing: 0; + border-collapse: collapse; + border-spacing: 0; } \ No newline at end of file diff --git a/src/styles/components/_tooltips.scss b/src/styles/components/_tooltips.scss index d736cdcfef..e5e4873ee4 100644 --- a/src/styles/components/_tooltips.scss +++ b/src/styles/components/_tooltips.scss @@ -1,5 +1,5 @@ div.MuiTooltip-tooltip { - font-size: 0.9rem; - outline: 2px solid transparent; - line-height: 2; + font-size: 0.9rem; + outline: 2px solid transparent; + line-height: 2; } diff --git a/src/styles/components/video/_video-editor.scss b/src/styles/components/video/_video-editor.scss index 88bd364e61..f61cc2eab8 100644 --- a/src/styles/components/video/_video-editor.scss +++ b/src/styles/components/video/_video-editor.scss @@ -490,27 +490,27 @@ $segment-deleted-selected: saturate(darken($segment-deleted, 25%), 5%); } .arrow_box:after, .arrow_box:before { - bottom: 100%; - left: 50%; - border: solid transparent; - content: " "; - height: 0; - width: 0; - position: absolute; - pointer-events: none; + bottom: 100%; + left: 50%; + border: solid transparent; + content: " "; + height: 0; + width: 0; + position: absolute; + pointer-events: none; } .arrow_box:after { - border-color: rgba(238, 238, 238, 0); - border-bottom-color: #eee; - border-width: 4px; - margin-left: -4px; + border-color: rgba(238, 238, 238, 0); + border-bottom-color: #eee; + border-width: 4px; + margin-left: -4px; } .arrow_box:before { - border-color: rgba(204, 204, 204, 0); - border-bottom-color: #ccc; - border-width: 5px; - margin-left: -5px; + border-color: rgba(204, 204, 204, 0); + border-bottom-color: #ccc; + border-width: 5px; + margin-left: -5px; } a.split { diff --git a/src/styles/mixins/_triangle-point.scss b/src/styles/mixins/_triangle-point.scss index 97ddceb310..6488f02ee5 100644 --- a/src/styles/mixins/_triangle-point.scss +++ b/src/styles/mixins/_triangle-point.scss @@ -35,7 +35,7 @@ width: 0; height: 0; border-style: solid; - border-width: 0 $width $height $width; + border-width: 0 $width $height $width; } &:after { @@ -60,7 +60,7 @@ width: 0; height: 0; border-style: solid; - border-width: 0 $width $height $width; + border-width: 0 $width $height $width; } &:after { diff --git a/src/thunks/assetsThunks.ts b/src/thunks/assetsThunks.ts index 180108b8ab..177b4f86e1 100644 --- a/src/thunks/assetsThunks.ts +++ b/src/thunks/assetsThunks.ts @@ -7,53 +7,53 @@ import { Publication } from "../slices/eventDetailsSlice"; // thunks for assets, especially for getting asset options export const fetchAssetUploadOptions = createAppAsyncThunk('assets/fetchAssetUploadOptionsAsyncThunk', async (_, { getState }) => { - // get old asset upload options - const state = getState(); - const assetUploadOptions = getAssetUploadOptions(state); - - const sourcePrefix = "EVENTS.EVENTS.NEW.SOURCE.UPLOAD"; - const assetPrefix = "EVENTS.EVENTS.NEW.UPLOAD_ASSET.OPTION"; - const workflowPrefix = "EVENTS.EVENTS.NEW.UPLOAD_ASSET.WORKFLOWDEFID"; - - // only fetch asset upload options, if they haven't been fetched yet - if (!(assetUploadOptions.length !== 0 && assetUploadOptions.length !== 0)) { - let workflow; - let newAssetUploadOptions: UploadAssetOption[] = []; - - // request asset upload options from API - await axios - .get("/admin-ng/resources/eventUploadAssetOptions.json") - .then((dataResponse) => { - // iterate over response and only use non-comment lines - for (const [optionKey, optionJson] of Object.entries( - dataResponse.data - )) { - if (optionKey.charAt(0) !== "$") { - const isSourceOption = optionKey.indexOf(sourcePrefix) >= 0; - const isAssetOption = optionKey.indexOf(assetPrefix) >= 0; - - // if the line is a source upload option or additional asset upload option, - // format it and add to upload options list - if (isSourceOption || isAssetOption) { - let option = JSON.parse(optionJson as string); - - option = { - ...option, - title: !option.title ? optionKey : option.title, - showAs: isSourceOption ? "source" : "uploadAsset", - }; - - newAssetUploadOptions.push(option); - } else if (optionKey.indexOf(workflowPrefix) >= 0) { - // if the line is the upload asset workflow id, set the asset upload workflow - workflow = optionJson as string; - } - } - } - }) - - return { workflow, newAssetUploadOptions }; - } + // get old asset upload options + const state = getState(); + const assetUploadOptions = getAssetUploadOptions(state); + + const sourcePrefix = "EVENTS.EVENTS.NEW.SOURCE.UPLOAD"; + const assetPrefix = "EVENTS.EVENTS.NEW.UPLOAD_ASSET.OPTION"; + const workflowPrefix = "EVENTS.EVENTS.NEW.UPLOAD_ASSET.WORKFLOWDEFID"; + + // only fetch asset upload options, if they haven't been fetched yet + if (!(assetUploadOptions.length !== 0 && assetUploadOptions.length !== 0)) { + let workflow; + let newAssetUploadOptions: UploadAssetOption[] = []; + + // request asset upload options from API + await axios + .get("/admin-ng/resources/eventUploadAssetOptions.json") + .then((dataResponse) => { + // iterate over response and only use non-comment lines + for (const [optionKey, optionJson] of Object.entries( + dataResponse.data + )) { + if (optionKey.charAt(0) !== "$") { + const isSourceOption = optionKey.indexOf(sourcePrefix) >= 0; + const isAssetOption = optionKey.indexOf(assetPrefix) >= 0; + + // if the line is a source upload option or additional asset upload option, + // format it and add to upload options list + if (isSourceOption || isAssetOption) { + let option = JSON.parse(optionJson as string); + + option = { + ...option, + title: !option.title ? optionKey : option.title, + showAs: isSourceOption ? "source" : "uploadAsset", + }; + + newAssetUploadOptions.push(option); + } else if (optionKey.indexOf(workflowPrefix) >= 0) { + // if the line is the upload asset workflow id, set the asset upload workflow + workflow = optionJson as string; + } + } + } + }) + + return { workflow, newAssetUploadOptions }; + } }); /** @@ -61,65 +61,65 @@ export const fetchAssetUploadOptions = createAppAsyncThunk('assets/fetchAssetUpl * The additional info is used for rendering purposes */ export const enrichPublications = createAppAsyncThunk('assets/enrichPublications', async ( - publications: { - publications: { - id: string, - name: string, - url: string, - }[], - "start-date"?: string, - "end-date"?: string, - }, + publications: { + publications: { + id: string, + name: string, + url: string, + }[], + "start-date"?: string, + "end-date"?: string, + }, ) => { - // get information about possible publication channels - let data = await axios.get("/admin-ng/resources/PUBLICATION.CHANNELS.json"); - - let publicationChannels: { [key: string]: string } = await data.data; - - let now = new Date(); - let combinedPublications: Publication[] = []; - - // fill publication objects with additional information - publications.publications.forEach((publication) => { - let newPublication: Publication = { - enabled: true, - id: publication.id, - name: publication.name, - order: 0, - url: publication.url, - }; - newPublication.enabled = (publications["start-date"] && publications["end-date"]) ? - !( - publication.id === "engage-live" && - (now < new Date(publications["start-date"]) || - now > new Date(publications["end-date"])) - ) - : - true; - - if (publicationChannels[publication.id]) { - let channel = JSON.parse(publicationChannels[publication.id]); - - if (channel.label) { - newPublication.label = channel.label; - } - if (channel.icon) { - newPublication.icon = channel.icon; - } - if (channel.hide) { - newPublication.hide = channel.hide; - } - if (channel.description) { - newPublication.description = channel.description; - } - if (channel.order) { - newPublication.order = channel.order; - } - } - combinedPublications.push(newPublication); - }); - - combinedPublications = combinedPublications.sort(({order: a}, {order:b}) => a - b); - - return combinedPublications; + // get information about possible publication channels + let data = await axios.get("/admin-ng/resources/PUBLICATION.CHANNELS.json"); + + let publicationChannels: { [key: string]: string } = await data.data; + + let now = new Date(); + let combinedPublications: Publication[] = []; + + // fill publication objects with additional information + publications.publications.forEach((publication) => { + let newPublication: Publication = { + enabled: true, + id: publication.id, + name: publication.name, + order: 0, + url: publication.url, + }; + newPublication.enabled = (publications["start-date"] && publications["end-date"]) ? + !( + publication.id === "engage-live" && + (now < new Date(publications["start-date"]) || + now > new Date(publications["end-date"])) + ) + : + true; + + if (publicationChannels[publication.id]) { + let channel = JSON.parse(publicationChannels[publication.id]); + + if (channel.label) { + newPublication.label = channel.label; + } + if (channel.icon) { + newPublication.icon = channel.icon; + } + if (channel.hide) { + newPublication.hide = channel.hide; + } + if (channel.description) { + newPublication.description = channel.description; + } + if (channel.order) { + newPublication.order = channel.order; + } + } + combinedPublications.push(newPublication); + }); + + combinedPublications = combinedPublications.sort(({order: a}, {order:b}) => a - b); + + return combinedPublications; }); \ No newline at end of file diff --git a/src/thunks/tableThunks.ts b/src/thunks/tableThunks.ts index 5b26fcc093..dad27d9251 100644 --- a/src/thunks/tableThunks.ts +++ b/src/thunks/tableThunks.ts @@ -9,30 +9,30 @@ import { groupsTableConfig } from "../configs/tableConfigs/groupsTableConfig"; import { TableConfig, aclsTableConfig } from "../configs/tableConfigs/aclsTableConfig"; import { themesTableConfig } from "../configs/tableConfigs/themesTableConfig"; import { - deselectAll, - loadResourceIntoTable, - selectAll, - selectRow, - setOffset, - setPageActive, - setPages, + deselectAll, + loadResourceIntoTable, + selectAll, + selectRow, + setOffset, + setPageActive, + setPages, } from "../slices/tableSlice"; import { - setEventColumns, - setShowActions as showEventsActions, - fetchEvents, + setEventColumns, + setShowActions as showEventsActions, + fetchEvents, } from "../slices/eventSlice"; import { - getPageOffset, - getResourceType, - getSelectedRows, - getTablePages, - getTablePagination, + getPageOffset, + getResourceType, + getSelectedRows, + getTablePages, + getTablePagination, } from "../selectors/tableSelectors"; import { - fetchSeries, - setSeriesColumns, - showActionsSeries, + fetchSeries, + setSeriesColumns, + showActionsSeries, } from "../slices/seriesSlice"; import { fetchJobs, setJobColumns } from "../slices/jobSlice"; import { fetchServers, setServerColumns } from "../slices/serverSlice"; @@ -51,646 +51,646 @@ import { AppDispatch, AppThunk, RootState } from "../store"; // Method to load events into the table export const loadEventsIntoTable = (): AppThunk => async (dispatch, getState) => { - const { events, table } = getState() as RootState; - const total = events.total; - - const pagination = table.pagination; - // check which events are currently selected - const resource = events.results.map((result) => { - const current = table.rows.find((row) => "id" in row && row.id === result.id); - - if (!!current && table.resource === "events") { - return { - ...result, - selected: current.selected, - }; - } else { - return { - ...result, - selected: false, - }; - } - }); - - const pages = calculatePages(total / pagination.limit, pagination.offset); - - let tableData = { - resource: "events" as const, - rows: resource, - columns: events.columns, - multiSelect: table.multiSelect, - pages: pages, - sortBy: table.sortBy["events"], - reverse: table.reverse["events"], - totalItems: total, - }; - - if (table.resource !== "events") { - const multiSelect = eventsTableConfig.multiSelect; - - tableData = { - ...tableData, - multiSelect: multiSelect, - }; - } - dispatch(loadResourceIntoTable(tableData)); + const { events, table } = getState() as RootState; + const total = events.total; + + const pagination = table.pagination; + // check which events are currently selected + const resource = events.results.map((result) => { + const current = table.rows.find((row) => "id" in row && row.id === result.id); + + if (!!current && table.resource === "events") { + return { + ...result, + selected: current.selected, + }; + } else { + return { + ...result, + selected: false, + }; + } + }); + + const pages = calculatePages(total / pagination.limit, pagination.offset); + + let tableData = { + resource: "events" as const, + rows: resource, + columns: events.columns, + multiSelect: table.multiSelect, + pages: pages, + sortBy: table.sortBy["events"], + reverse: table.reverse["events"], + totalItems: total, + }; + + if (table.resource !== "events") { + const multiSelect = eventsTableConfig.multiSelect; + + tableData = { + ...tableData, + multiSelect: multiSelect, + }; + } + dispatch(loadResourceIntoTable(tableData)); }; // Method to load series into the table export const loadSeriesIntoTable = (): AppThunk => (dispatch, getState) => { - const { series, table } = getState() as RootState; - const total = series.total; - const pagination = table.pagination; - - // check which events are currently selected - const resource = series.results.map((result) => { - const current = table.rows.find((row) => "id" in row && row.id === result.id); - - if (!!current && table.resource === "series") { - return { - ...result, - selected: current.selected, - }; - } else { - return { - ...result, - selected: false, - }; - } - }); - - const pages = calculatePages(total / pagination.limit, pagination.offset); - - let tableData = { - resource: "series" as const, - rows: resource, - columns: series.columns, - multiSelect: table.multiSelect, - pages: pages, - sortBy: table.sortBy["series"], - reverse: table.reverse["series"], - totalItems: total, - }; - - if (table.resource !== "series") { - const multiSelect = seriesTableConfig.multiSelect; - - tableData = { - ...tableData, - multiSelect: multiSelect, - }; - } - dispatch(loadResourceIntoTable(tableData)); + const { series, table } = getState() as RootState; + const total = series.total; + const pagination = table.pagination; + + // check which events are currently selected + const resource = series.results.map((result) => { + const current = table.rows.find((row) => "id" in row && row.id === result.id); + + if (!!current && table.resource === "series") { + return { + ...result, + selected: current.selected, + }; + } else { + return { + ...result, + selected: false, + }; + } + }); + + const pages = calculatePages(total / pagination.limit, pagination.offset); + + let tableData = { + resource: "series" as const, + rows: resource, + columns: series.columns, + multiSelect: table.multiSelect, + pages: pages, + sortBy: table.sortBy["series"], + reverse: table.reverse["series"], + totalItems: total, + }; + + if (table.resource !== "series") { + const multiSelect = seriesTableConfig.multiSelect; + + tableData = { + ...tableData, + multiSelect: multiSelect, + }; + } + dispatch(loadResourceIntoTable(tableData)); }; export const loadRecordingsIntoTable = (): AppThunk => (dispatch, getState) => { - const { recordings, table } = getState() as RootState; - const pagination = table.pagination; - const resource = recordings.results; - const total = recordings.total; - - const pages = calculatePages(total / pagination.limit, pagination.offset); - - let tableData = { - resource: "recordings" as const, - columns: recordings.columns, - multiSelect: table.multiSelect, - pages: pages, - sortBy: table.sortBy["recordings"], - reverse: table.reverse["recordings"], - rows: resource.map((obj) => { - return { ...obj, selected: false } - }), - totalItems: total, - }; - - if (table.resource !== "recordings") { - const multiSelect = recordingsTableConfig.multiSelect; - - tableData = { - ...tableData, - multiSelect: multiSelect, - }; - } - - dispatch(loadResourceIntoTable(tableData)); + const { recordings, table } = getState() as RootState; + const pagination = table.pagination; + const resource = recordings.results; + const total = recordings.total; + + const pages = calculatePages(total / pagination.limit, pagination.offset); + + let tableData = { + resource: "recordings" as const, + columns: recordings.columns, + multiSelect: table.multiSelect, + pages: pages, + sortBy: table.sortBy["recordings"], + reverse: table.reverse["recordings"], + rows: resource.map((obj) => { + return { ...obj, selected: false } + }), + totalItems: total, + }; + + if (table.resource !== "recordings") { + const multiSelect = recordingsTableConfig.multiSelect; + + tableData = { + ...tableData, + multiSelect: multiSelect, + }; + } + + dispatch(loadResourceIntoTable(tableData)); }; export const loadJobsIntoTable = (): AppThunk => (dispatch, getState) => { - const { jobs, table } = getState() as RootState; - const pagination = table.pagination; - const resource = jobs.results; - const total = jobs.total; - - const pages = calculatePages(total / pagination.limit, pagination.offset); - - let tableData = { - resource: "jobs" as const, - rows: resource.map((obj) => { - return { ...obj, selected: false } - }), - columns: jobs.columns, - multiSelect: table.multiSelect, - pages: pages, - sortBy: table.sortBy["jobs"], - reverse: table.reverse["jobs"], - totalItems: total, - }; - - if (table.resource !== "jobs") { - const multiSelect = jobsTableConfig.multiSelect; - - tableData = { - ...tableData, - multiSelect: multiSelect, - }; - } - dispatch(loadResourceIntoTable(tableData)); + const { jobs, table } = getState() as RootState; + const pagination = table.pagination; + const resource = jobs.results; + const total = jobs.total; + + const pages = calculatePages(total / pagination.limit, pagination.offset); + + let tableData = { + resource: "jobs" as const, + rows: resource.map((obj) => { + return { ...obj, selected: false } + }), + columns: jobs.columns, + multiSelect: table.multiSelect, + pages: pages, + sortBy: table.sortBy["jobs"], + reverse: table.reverse["jobs"], + totalItems: total, + }; + + if (table.resource !== "jobs") { + const multiSelect = jobsTableConfig.multiSelect; + + tableData = { + ...tableData, + multiSelect: multiSelect, + }; + } + dispatch(loadResourceIntoTable(tableData)); }; export const loadServersIntoTable = (): AppThunk => (dispatch, getState) => { - const { servers, table } = getState() as RootState; - const pagination = table.pagination; - const resource = servers.results; - const total = servers.total; - - const pages = calculatePages(total / pagination.limit, pagination.offset); - - let tableData = { - resource: "servers" as const, - rows: resource.map((obj) => { - return { ...obj, selected: false } - }), - columns: servers.columns, - multiSelect: table.multiSelect, - pages: pages, - sortBy: table.sortBy["servers"], - reverse: table.reverse["servers"], - totalItems: total, - }; - - if (table.resource !== "servers") { - const multiSelect = serversTableConfig.multiSelect; - - tableData = { - ...tableData, - multiSelect: multiSelect, - }; - } - dispatch(loadResourceIntoTable(tableData)); + const { servers, table } = getState() as RootState; + const pagination = table.pagination; + const resource = servers.results; + const total = servers.total; + + const pages = calculatePages(total / pagination.limit, pagination.offset); + + let tableData = { + resource: "servers" as const, + rows: resource.map((obj) => { + return { ...obj, selected: false } + }), + columns: servers.columns, + multiSelect: table.multiSelect, + pages: pages, + sortBy: table.sortBy["servers"], + reverse: table.reverse["servers"], + totalItems: total, + }; + + if (table.resource !== "servers") { + const multiSelect = serversTableConfig.multiSelect; + + tableData = { + ...tableData, + multiSelect: multiSelect, + }; + } + dispatch(loadResourceIntoTable(tableData)); }; export const loadServicesIntoTable = (): AppThunk => (dispatch, getState) => { - const { services, table } = getState() as RootState; - const pagination = table.pagination; - const resource = services.results; - const total = services.total; - - const pages = calculatePages(total / pagination.limit, pagination.offset); - - let tableData = { - rows: resource.map((obj) => { - return { ...obj, selected: false } - }), - pages: pages, - totalItems: total, - resource: "services" as const, - columns: services.columns, - multiSelect: table.multiSelect, - sortBy: table.sortBy["services"], - reverse: table.reverse["services"], - }; - - if (table.resource !== "services") { - const multiSelect = servicesTableConfig.multiSelect; - - tableData = { - ...tableData, - multiSelect: multiSelect, - }; - } - - dispatch(loadResourceIntoTable(tableData)); + const { services, table } = getState() as RootState; + const pagination = table.pagination; + const resource = services.results; + const total = services.total; + + const pages = calculatePages(total / pagination.limit, pagination.offset); + + let tableData = { + rows: resource.map((obj) => { + return { ...obj, selected: false } + }), + pages: pages, + totalItems: total, + resource: "services" as const, + columns: services.columns, + multiSelect: table.multiSelect, + sortBy: table.sortBy["services"], + reverse: table.reverse["services"], + }; + + if (table.resource !== "services") { + const multiSelect = servicesTableConfig.multiSelect; + + tableData = { + ...tableData, + multiSelect: multiSelect, + }; + } + + dispatch(loadResourceIntoTable(tableData)); }; export const loadUsersIntoTable = (): AppThunk => (dispatch, getState) => { - const { users, table } = getState() as RootState; - const pagination = table.pagination; - const resource = users.results; - const total = users.total; - - const pages = calculatePages(total / pagination.limit, pagination.offset); - - let tableData = { - resource: "users" as const, - rows: resource.map((obj) => { - return { ...obj, selected: false } - }), - columns: users.columns, - multiSelect: table.multiSelect, - pages: pages, - sortBy: table.sortBy["users"], - reverse: table.reverse["users"], - totalItems: total, - }; - - if (table.resource !== "users") { - const multiSelect = usersTableConfig.multiSelect; - - tableData = { - ...tableData, - multiSelect: multiSelect, - }; - } - dispatch(loadResourceIntoTable(tableData)); + const { users, table } = getState() as RootState; + const pagination = table.pagination; + const resource = users.results; + const total = users.total; + + const pages = calculatePages(total / pagination.limit, pagination.offset); + + let tableData = { + resource: "users" as const, + rows: resource.map((obj) => { + return { ...obj, selected: false } + }), + columns: users.columns, + multiSelect: table.multiSelect, + pages: pages, + sortBy: table.sortBy["users"], + reverse: table.reverse["users"], + totalItems: total, + }; + + if (table.resource !== "users") { + const multiSelect = usersTableConfig.multiSelect; + + tableData = { + ...tableData, + multiSelect: multiSelect, + }; + } + dispatch(loadResourceIntoTable(tableData)); }; export const loadGroupsIntoTable = (): AppThunk => (dispatch, getState) => { - const { groups, table } = getState() as RootState; - const pagination = table.pagination; - const resource = groups.results; - const total = groups.total; - - const pages = calculatePages(total / pagination.limit, pagination.offset); - - let tableData = { - resource: "groups" as const, - rows: resource.map((obj) => { - return { ...obj, selected: false } - }), - columns: groups.columns, - multiSelect: table.multiSelect, - pages: pages, - sortBy: table.sortBy["groups"], - reverse: table.reverse["groups"], - totalItems: total, - }; - - if (table.resource !== "groups") { - const multiSelect = groupsTableConfig.multiSelect; - - tableData = { - ...tableData, - multiSelect: multiSelect, - }; - } - dispatch(loadResourceIntoTable(tableData)); + const { groups, table } = getState() as RootState; + const pagination = table.pagination; + const resource = groups.results; + const total = groups.total; + + const pages = calculatePages(total / pagination.limit, pagination.offset); + + let tableData = { + resource: "groups" as const, + rows: resource.map((obj) => { + return { ...obj, selected: false } + }), + columns: groups.columns, + multiSelect: table.multiSelect, + pages: pages, + sortBy: table.sortBy["groups"], + reverse: table.reverse["groups"], + totalItems: total, + }; + + if (table.resource !== "groups") { + const multiSelect = groupsTableConfig.multiSelect; + + tableData = { + ...tableData, + multiSelect: multiSelect, + }; + } + dispatch(loadResourceIntoTable(tableData)); }; export const loadAclsIntoTable = (): AppThunk => (dispatch, getState) => { - const { acls, table } = getState() as RootState; - const pagination = table.pagination; - const resource = acls.results; - const total = acls.total; - - const pages = calculatePages(total / pagination.limit, pagination.offset); - - let tableData = { - resource: "acls" as const, - rows: resource.map((obj) => { - return { ...obj, selected: false } - }), - columns: acls.columns, - multiSelect: table.multiSelect, - pages: pages, - sortBy: table.sortBy["acls"], - reverse: table.reverse["acls"], - totalItems: total, - }; - - if (table.resource !== "acls") { - const multiSelect = aclsTableConfig.multiSelect; - tableData = { - ...tableData, - multiSelect: multiSelect, - }; - } - dispatch(loadResourceIntoTable(tableData)); + const { acls, table } = getState() as RootState; + const pagination = table.pagination; + const resource = acls.results; + const total = acls.total; + + const pages = calculatePages(total / pagination.limit, pagination.offset); + + let tableData = { + resource: "acls" as const, + rows: resource.map((obj) => { + return { ...obj, selected: false } + }), + columns: acls.columns, + multiSelect: table.multiSelect, + pages: pages, + sortBy: table.sortBy["acls"], + reverse: table.reverse["acls"], + totalItems: total, + }; + + if (table.resource !== "acls") { + const multiSelect = aclsTableConfig.multiSelect; + tableData = { + ...tableData, + multiSelect: multiSelect, + }; + } + dispatch(loadResourceIntoTable(tableData)); }; export const loadThemesIntoTable = (): AppThunk => (dispatch, getState) => { - const { themes, table } = getState() as RootState; - const pagination = table.pagination; - const resource = themes.results; - const total = themes.total; - - const pages = calculatePages(total / pagination.limit, pagination.offset); - - let tableData = { - resource: "themes" as const, - rows: resource.map((obj) => { - return { ...obj, selected: false } - }), - columns: themes.columns, - multiSelect: table.multiSelect, - pages: pages, - sortBy: table.sortBy["themes"], - reverse: table.reverse["themes"], - totalItems: total, - }; - - if (table.resource !== "themes") { - const multiSelect = themesTableConfig.multiSelect; - - tableData = { - ...tableData, - multiSelect: multiSelect, - }; - } - dispatch(loadResourceIntoTable(tableData)); + const { themes, table } = getState() as RootState; + const pagination = table.pagination; + const resource = themes.results; + const total = themes.total; + + const pages = calculatePages(total / pagination.limit, pagination.offset); + + let tableData = { + resource: "themes" as const, + rows: resource.map((obj) => { + return { ...obj, selected: false } + }), + columns: themes.columns, + multiSelect: table.multiSelect, + pages: pages, + sortBy: table.sortBy["themes"], + reverse: table.reverse["themes"], + totalItems: total, + }; + + if (table.resource !== "themes") { + const multiSelect = themesTableConfig.multiSelect; + + tableData = { + ...tableData, + multiSelect: multiSelect, + }; + } + dispatch(loadResourceIntoTable(tableData)); }; // Navigate between pages export const goToPage = (pageNumber: number) => async (dispatch: AppDispatch, getState: () => RootState) => { - dispatch(deselectAll()); - dispatch(setOffset(pageNumber)); - - const state = getState(); - const offset = getPageOffset(state); - const pages = getTablePages(state); - - if (pages) { - dispatch(setPageActive(offset ? pages[offset].number : pageNumber)); - } - - // Get resources of page and load them into table - // eslint-disable-next-line default-case - switch (getResourceType(state)) { - case "events": { - await dispatch(fetchEvents()); - dispatch(loadEventsIntoTable()); - break; - } - case "series": { - await dispatch(fetchSeries()); - dispatch(loadSeriesIntoTable()); - break; - } - case "recordings": { - await dispatch(fetchRecordings()); - dispatch(loadRecordingsIntoTable()); - break; - } - case "jobs": { - await dispatch(fetchJobs()); - dispatch(loadJobsIntoTable()); - break; - } - case "servers": { - await dispatch(fetchServers()); - dispatch(loadServersIntoTable()); - break; - } - case "services": { - await dispatch(fetchServices()); - dispatch(loadServicesIntoTable()); - break; - } - case "users": { - await dispatch(fetchUsers()); - dispatch(loadUsersIntoTable()); - break; - } - case "groups": { - await dispatch(fetchGroups()); - dispatch(loadGroupsIntoTable()); - break; - } - case "acls": { - await dispatch(fetchAcls()); - dispatch(loadAclsIntoTable()); - break; - } - case "themes": { - await dispatch(fetchThemes()); - dispatch(loadThemesIntoTable()); - break; - } - } + dispatch(deselectAll()); + dispatch(setOffset(pageNumber)); + + const state = getState(); + const offset = getPageOffset(state); + const pages = getTablePages(state); + + if (pages) { + dispatch(setPageActive(offset ? pages[offset].number : pageNumber)); + } + + // Get resources of page and load them into table + // eslint-disable-next-line default-case + switch (getResourceType(state)) { + case "events": { + await dispatch(fetchEvents()); + dispatch(loadEventsIntoTable()); + break; + } + case "series": { + await dispatch(fetchSeries()); + dispatch(loadSeriesIntoTable()); + break; + } + case "recordings": { + await dispatch(fetchRecordings()); + dispatch(loadRecordingsIntoTable()); + break; + } + case "jobs": { + await dispatch(fetchJobs()); + dispatch(loadJobsIntoTable()); + break; + } + case "servers": { + await dispatch(fetchServers()); + dispatch(loadServersIntoTable()); + break; + } + case "services": { + await dispatch(fetchServices()); + dispatch(loadServicesIntoTable()); + break; + } + case "users": { + await dispatch(fetchUsers()); + dispatch(loadUsersIntoTable()); + break; + } + case "groups": { + await dispatch(fetchGroups()); + dispatch(loadGroupsIntoTable()); + break; + } + case "acls": { + await dispatch(fetchAcls()); + dispatch(loadAclsIntoTable()); + break; + } + case "themes": { + await dispatch(fetchThemes()); + dispatch(loadThemesIntoTable()); + break; + } + } }; // Update pages for example if page size was changed export const updatePages = () => async (dispatch: AppDispatch, getState: () => RootState) => { - const state = getState() as RootState; - - const pagination = getTablePagination(state); - - const pages = calculatePages( - pagination.totalItems / pagination.limit, - pagination.offset - ); - - dispatch(setPages(pages)); - - // Get resources of page and load them into table - // eslint-disable-next-line default-case - switch (getResourceType(state)) { - case "events": { - await dispatch(fetchEvents()); - dispatch(loadEventsIntoTable()); - break; - } - case "series": { - await dispatch(fetchSeries()); - dispatch(loadSeriesIntoTable()); - break; - } - case "recordings": { - await dispatch(fetchRecordings()); - dispatch(loadRecordingsIntoTable()); - break; - } - case "jobs": { - await dispatch(fetchJobs()); - dispatch(loadJobsIntoTable()); - break; - } - case "servers": { - await dispatch(fetchServers()); - dispatch(loadServersIntoTable()); - break; - } - case "services": { - await dispatch(fetchServices()); - dispatch(loadServicesIntoTable()); - break; - } - case "users": { - await dispatch(fetchUsers()); - dispatch(loadUsersIntoTable()); - break; - } - case "groups": { - await dispatch(fetchGroups()); - dispatch(loadGroupsIntoTable()); - break; - } - case "acls": { - await dispatch(fetchAcls()); - dispatch(loadAclsIntoTable()); - break; - } - case "themes": { - await dispatch(fetchThemes()); - dispatch(loadThemesIntoTable()); - break; - } - } + const state = getState() as RootState; + + const pagination = getTablePagination(state); + + const pages = calculatePages( + pagination.totalItems / pagination.limit, + pagination.offset + ); + + dispatch(setPages(pages)); + + // Get resources of page and load them into table + // eslint-disable-next-line default-case + switch (getResourceType(state)) { + case "events": { + await dispatch(fetchEvents()); + dispatch(loadEventsIntoTable()); + break; + } + case "series": { + await dispatch(fetchSeries()); + dispatch(loadSeriesIntoTable()); + break; + } + case "recordings": { + await dispatch(fetchRecordings()); + dispatch(loadRecordingsIntoTable()); + break; + } + case "jobs": { + await dispatch(fetchJobs()); + dispatch(loadJobsIntoTable()); + break; + } + case "servers": { + await dispatch(fetchServers()); + dispatch(loadServersIntoTable()); + break; + } + case "services": { + await dispatch(fetchServices()); + dispatch(loadServicesIntoTable()); + break; + } + case "users": { + await dispatch(fetchUsers()); + dispatch(loadUsersIntoTable()); + break; + } + case "groups": { + await dispatch(fetchGroups()); + dispatch(loadGroupsIntoTable()); + break; + } + case "acls": { + await dispatch(fetchAcls()); + dispatch(loadAclsIntoTable()); + break; + } + case "themes": { + await dispatch(fetchThemes()); + dispatch(loadThemesIntoTable()); + break; + } + } }; // Select all rows on table page export const changeAllSelected = (selected: boolean): AppThunk => (dispatch, getState) => { - const state = getState(); - - if (selected) { - // eslint-disable-next-line default-case - switch (getResourceType(state)) { - case "events": { - dispatch(showEventsActions(true)); - break; - } - case "series": { - dispatch(showActionsSeries(true)); - break; - } - } - dispatch(selectAll()); - } else { - // eslint-disable-next-line default-case - switch (getResourceType(state)) { - case "events": { - dispatch(showEventsActions(false)); - break; - } - case "series": { - dispatch(showActionsSeries(false)); - break; - } - } - dispatch(deselectAll()); - } + const state = getState(); + + if (selected) { + // eslint-disable-next-line default-case + switch (getResourceType(state)) { + case "events": { + dispatch(showEventsActions(true)); + break; + } + case "series": { + dispatch(showActionsSeries(true)); + break; + } + } + dispatch(selectAll()); + } else { + // eslint-disable-next-line default-case + switch (getResourceType(state)) { + case "events": { + dispatch(showEventsActions(false)); + break; + } + case "series": { + dispatch(showActionsSeries(false)); + break; + } + } + dispatch(deselectAll()); + } }; // Select certain columns export const changeColumnSelection = (updatedColumns: TableConfig["columns"]) => async ( - dispatch: AppDispatch, getState: () => RootState + dispatch: AppDispatch, getState: () => RootState ) => { - const state = getState(); - - // eslint-disable-next-line default-case - switch (getResourceType(state)) { - case "events": { - await dispatch(setEventColumns(updatedColumns)); - - if (getSelectedRows(state).length > 0) { - dispatch(showEventsActions(true)); - } else { - dispatch(showEventsActions(false)); - } - - dispatch(loadEventsIntoTable()); - - await dispatch(fetchEvents()); - dispatch(loadEventsIntoTable()); - - break; - } - case "series": { - await dispatch(setSeriesColumns(updatedColumns)); - - if (getSelectedRows(state).length > 0) { - dispatch(showActionsSeries(true)); - } else { - dispatch(showActionsSeries(false)); - } - - dispatch(loadSeriesIntoTable()); - break; - } - case "recordings": { - await dispatch(setRecordingsColumns(updatedColumns)); - dispatch(loadRecordingsIntoTable()); - break; - } - case "jobs": { - await dispatch(setJobColumns(updatedColumns)); - dispatch(loadJobsIntoTable()); - break; - } - case "servers": { - await dispatch(setServerColumns(updatedColumns)); - dispatch(loadServersIntoTable()); - break; - } - case "services": { - await dispatch(setServiceColumns(updatedColumns)); - dispatch(loadServicesIntoTable()); - break; - } - case "users": { - await dispatch(setUserColumns(updatedColumns)); - dispatch(loadUsersIntoTable()); - break; - } - case "groups": { - await dispatch(setGroupColumns(updatedColumns)); - dispatch(loadGroupsIntoTable()); - break; - } - case "acls": { - await dispatch(setAclColumns(updatedColumns)); - dispatch(loadAclsIntoTable()); - break; - } - case "themes": { - await dispatch(setThemeColumns(updatedColumns)); - dispatch(loadThemesIntoTable()); - break; - } - } + const state = getState(); + + // eslint-disable-next-line default-case + switch (getResourceType(state)) { + case "events": { + await dispatch(setEventColumns(updatedColumns)); + + if (getSelectedRows(state).length > 0) { + dispatch(showEventsActions(true)); + } else { + dispatch(showEventsActions(false)); + } + + dispatch(loadEventsIntoTable()); + + await dispatch(fetchEvents()); + dispatch(loadEventsIntoTable()); + + break; + } + case "series": { + await dispatch(setSeriesColumns(updatedColumns)); + + if (getSelectedRows(state).length > 0) { + dispatch(showActionsSeries(true)); + } else { + dispatch(showActionsSeries(false)); + } + + dispatch(loadSeriesIntoTable()); + break; + } + case "recordings": { + await dispatch(setRecordingsColumns(updatedColumns)); + dispatch(loadRecordingsIntoTable()); + break; + } + case "jobs": { + await dispatch(setJobColumns(updatedColumns)); + dispatch(loadJobsIntoTable()); + break; + } + case "servers": { + await dispatch(setServerColumns(updatedColumns)); + dispatch(loadServersIntoTable()); + break; + } + case "services": { + await dispatch(setServiceColumns(updatedColumns)); + dispatch(loadServicesIntoTable()); + break; + } + case "users": { + await dispatch(setUserColumns(updatedColumns)); + dispatch(loadUsersIntoTable()); + break; + } + case "groups": { + await dispatch(setGroupColumns(updatedColumns)); + dispatch(loadGroupsIntoTable()); + break; + } + case "acls": { + await dispatch(setAclColumns(updatedColumns)); + dispatch(loadAclsIntoTable()); + break; + } + case "themes": { + await dispatch(setThemeColumns(updatedColumns)); + dispatch(loadThemesIntoTable()); + break; + } + } }; // Select certain row export const changeRowSelection = (id: number | string, selected: boolean): AppThunk => (dispatch, getState) => { - dispatch(selectRow(id)); - - const state = getState(); - - // eslint-disable-next-line default-case - switch (getResourceType(state)) { - case "events": { - if (getSelectedRows(state).length > 0) { - dispatch(showEventsActions(true)); - } else { - dispatch(showEventsActions(false)); - } - break; - } - case "series": { - if (getSelectedRows(state).length > 0) { - dispatch(showActionsSeries(true)); - } else { - dispatch(showActionsSeries(false)); - } - break; - } - } + dispatch(selectRow(id)); + + const state = getState(); + + // eslint-disable-next-line default-case + switch (getResourceType(state)) { + case "events": { + if (getSelectedRows(state).length > 0) { + dispatch(showEventsActions(true)); + } else { + dispatch(showEventsActions(false)); + } + break; + } + case "series": { + if (getSelectedRows(state).length > 0) { + dispatch(showActionsSeries(true)); + } else { + dispatch(showActionsSeries(false)); + } + break; + } + } }; const calculatePages = (numberOfPages: number, offset: number) => { - const pages = []; - - for (let i = 0; i < numberOfPages || (i === 0 && numberOfPages === 0); i++) { - pages.push({ - number: i, - label: (i + 1).toString(), - active: i === offset, - }); - } + const pages = []; + + for (let i = 0; i < numberOfPages || (i === 0 && numberOfPages === 0); i++) { + pages.push({ + number: i, + label: (i + 1).toString(), + active: i === offset, + }); + } if (pages.every(page => page.active === false)) { pages[0].active = true; } - return pages; + return pages; }; diff --git a/src/thunks/taskThunks.ts b/src/thunks/taskThunks.ts index cf3ca42e1e..1e3768cacf 100644 --- a/src/thunks/taskThunks.ts +++ b/src/thunks/taskThunks.ts @@ -4,45 +4,45 @@ import { AppDispatch } from "../store"; import { Event } from "../slices/eventSlice"; export const postTasks = ( - values: { - events: Event[] - configuration: { [key: string] : string } - workflow: string - } + values: { + events: Event[] + configuration: { [key: string] : string } + workflow: string + } ) => async (dispatch: AppDispatch) => { - let configuration: { [key: string] : string } = {}; - Object.keys(values.configuration).forEach((config) => { - configuration[config] = String(values.configuration[config]); - }); + let configuration: { [key: string] : string } = {}; + Object.keys(values.configuration).forEach((config) => { + configuration[config] = String(values.configuration[config]); + }); - let workflowConfig: { [key: string] : { [key: string] : string } } = {}; - for (let i = 0; i < values.events.length; i++) { - if (values.events[i].selected) { - let eventId = values.events[i].id; - workflowConfig[eventId] = configuration; - } - } + let workflowConfig: { [key: string] : { [key: string] : string } } = {}; + for (let i = 0; i < values.events.length; i++) { + if (values.events[i].selected) { + let eventId = values.events[i].id; + workflowConfig[eventId] = configuration; + } + } - let metadataJson = { - workflow: values.workflow, - configuration: workflowConfig, - }; + let metadataJson = { + workflow: values.workflow, + configuration: workflowConfig, + }; - let data = new URLSearchParams(); - data.append("metadata", JSON.stringify(metadataJson)); + let data = new URLSearchParams(); + data.append("metadata", JSON.stringify(metadataJson)); - axios - .post("/admin-ng/tasks/new", data, { - headers: { - "Content-Type": "application/x-www-form-urlencoded", - }, - }) - .then((response) => { - console.info(response); - dispatch(addNotification({type: "success", key: "TASK_CREATED"})); - }) - .catch((response) => { - console.error(response); - dispatch(addNotification({type: "error", key: "TASK_NOT_CREATED"})); - }); + axios + .post("/admin-ng/tasks/new", data, { + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + }) + .then((response) => { + console.info(response); + dispatch(addNotification({type: "success", key: "TASK_CREATED"})); + }) + .catch((response) => { + console.error(response); + dispatch(addNotification({type: "error", key: "TASK_NOT_CREATED"})); + }); }; diff --git a/src/utils/aclUtils.ts b/src/utils/aclUtils.ts index dc4b1e1545..3f1c7bd44f 100644 --- a/src/utils/aclUtils.ts +++ b/src/utils/aclUtils.ts @@ -4,24 +4,24 @@ import { TransformedAcl } from "../slices/aclDetailsSlice"; import { Role } from "../slices/aclSlice"; export const getAclTemplateText = ( - aclTemplates: { - id: string - value: string - }[], - formikTemplate: string + aclTemplates: { + id: string + value: string + }[], + formikTemplate: string ) => { - if (!!aclTemplates && aclTemplates.length > 0) { - const template = aclTemplates.find( - (template) => formikTemplate === template.id - ); - return !!template ? template.value : ""; - } else { - return ""; - } + if (!!aclTemplates && aclTemplates.length > 0) { + const template = aclTemplates.find( + (template) => formikTemplate === template.id + ); + return !!template ? template.value : ""; + } else { + return ""; + } }; export const filterRoles = (roles: Role[], policies: TransformedAcl[]) => { - return roles.filter( - (role) => !policies.find((policy) => policy.role === role.name) - ); + return roles.filter( + (role) => !policies.find((policy) => policy.role === role.name) + ); }; diff --git a/src/utils/adopterRegistrationUtils.ts b/src/utils/adopterRegistrationUtils.ts index 844d52cf4b..7b48a7576c 100644 --- a/src/utils/adopterRegistrationUtils.ts +++ b/src/utils/adopterRegistrationUtils.ts @@ -2,74 +2,74 @@ import axios from "axios"; // get information about adopter export const fetchAdopterRegistration = async () => { - // fetch current information about adopter - const response = await axios.get("/admin-ng/adopter/registration"); + // fetch current information about adopter + const response = await axios.get("/admin-ng/adopter/registration"); - return await response.data; + return await response.data; }; // get statistics information about adopter export const fetchAdopterStatisticsSummary = async () => { - const response = await axios.get("/admin-ng/adopter/summary"); + const response = await axios.get("/admin-ng/adopter/summary"); - return await response.data; + return await response.data; }; export type Registration = { - contactMe: boolean, - systemType: string, - allowsStatistics: boolean, - allowsErrorReports: boolean, - organisationName: string, - departmentName: string, - country: string, - postalCode: string, - city: string, - firstName: string, - lastName: string, - street: string, - streetNo: string, - email: string, - agreedToPolicy: boolean, + contactMe: boolean, + systemType: string, + allowsStatistics: boolean, + allowsErrorReports: boolean, + organisationName: string, + departmentName: string, + country: string, + postalCode: string, + city: string, + firstName: string, + lastName: string, + street: string, + streetNo: string, + email: string, + agreedToPolicy: boolean, } // post request for adopter information export const postRegistration = async ( - values: Registration + values: Registration ) => { - // build body - let body = new URLSearchParams(); - body.append("contactMe", values.contactMe.toString()); - body.append("systemType", values.systemType); - body.append("allowsStatistics", values.allowsStatistics.toString()); - body.append("allowsErrorReports", values.allowsErrorReports.toString()); - body.append("organisationName", values.organisationName); - body.append("departmentName", values.departmentName); - body.append("country", values.country); - body.append("postalCode", values.postalCode); - body.append("city", values.city); - body.append("firstName", values.firstName); - body.append("lastName", values.lastName); - body.append("street", values.street); - body.append("streetNo", values.streetNo); - body.append("email", values.email); - body.append("agreedToPolicy", values.agreedToPolicy.toString()); - body.append("registered", "true"); + // build body + let body = new URLSearchParams(); + body.append("contactMe", values.contactMe.toString()); + body.append("systemType", values.systemType); + body.append("allowsStatistics", values.allowsStatistics.toString()); + body.append("allowsErrorReports", values.allowsErrorReports.toString()); + body.append("organisationName", values.organisationName); + body.append("departmentName", values.departmentName); + body.append("country", values.country); + body.append("postalCode", values.postalCode); + body.append("city", values.city); + body.append("firstName", values.firstName); + body.append("lastName", values.lastName); + body.append("street", values.street); + body.append("streetNo", values.streetNo); + body.append("email", values.email); + body.append("agreedToPolicy", values.agreedToPolicy.toString()); + body.append("registered", "true"); - // save adopter information and return next state - await axios.post("/admin-ng/adopter/registration", body, { - headers: { - "Content-Type": "application/x-www-form-urlencoded", - }, - }); + // save adopter information and return next state + await axios.post("/admin-ng/adopter/registration", body, { + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + }); }; // delete adopter information export const deleteAdopterRegistration = async () => { - // delete adopter information - await axios.delete("/admin-ng/adopter/registration", { - headers: { - "Content-Type": "application/x-www-form-urlencoded", - }, - }); + // delete adopter information + await axios.delete("/admin-ng/adopter/registration", { + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + }); }; diff --git a/src/utils/bulkActionUtils.ts b/src/utils/bulkActionUtils.ts index 9743842304..70ff4281b0 100644 --- a/src/utils/bulkActionUtils.ts +++ b/src/utils/bulkActionUtils.ts @@ -8,140 +8,140 @@ import { AppDispatch } from "../store"; // Check if event is scheduled and therefore the schedule is editable export const isScheduleEditable = (event: Event) => { - return ( - event.event_status.toUpperCase().indexOf("SCHEDULED") > -1 || - !event.selected - ); + return ( + event.event_status.toUpperCase().indexOf("SCHEDULED") > -1 || + !event.selected + ); }; // Check if multiple events are scheduled and therefore the schedule is editable export const isAllScheduleEditable = (events: Event[]) => { - for (let i = 0; i < events.length; i++) { - if (!isScheduleEditable(events[i])) { - return false; - } - } - return true; + for (let i = 0; i < events.length; i++) { + if (!isScheduleEditable(events[i])) { + return false; + } + } + return true; }; // Check if user has access rights for capture agent of event export const isAgentAccess = (event: Event, user: UserInfoState) => { - return !event.selected || hasDeviceAccess(user, event.agent_id); + return !event.selected || hasDeviceAccess(user, event.agent_id); }; // Check if user has access rights for capture agent of several events export const isAllAgentAccess = (events: Event[], user: UserInfoState) => { - for (let i = 0; i < events.length; i++) { - if (!events[i].selected || !isScheduleEditable(events[i])) { - continue; - } - if (!isAgentAccess(events[i], user)) { - return false; - } - } - return true; + for (let i = 0; i < events.length; i++) { + if (!events[i].selected || !isScheduleEditable(events[i])) { + continue; + } + if (!isAgentAccess(events[i], user)) { + return false; + } + } + return true; }; // Check validity of selected event list for bulk schedule update for activating next button export const checkValidityUpdateScheduleEventSelection = ( - formikValues: { - events: Event[] - }, - user: UserInfoState + formikValues: { + events: Event[] + }, + user: UserInfoState ) => { - if (formikValues.events.length > 0) { - if ( - isAllScheduleEditable(formikValues.events) && - isAllAgentAccess(formikValues.events, user) - ) { - return formikValues.events.some((event) => event.selected === true); - } else { - return false; - } - } else { - return false; - } + if (formikValues.events.length > 0) { + if ( + isAllScheduleEditable(formikValues.events) && + isAllAgentAccess(formikValues.events, user) + ) { + return formikValues.events.some((event) => event.selected === true); + } else { + return false; + } + } else { + return false; + } }; // check changed events in formik for scheduling conflicts export const checkSchedulingConflicts = async ( - formikValues: { - editedEvents: EditedEvents[] - }, - setConflicts: (conflicts: Conflict[]) => void, - dispatch: AppDispatch, + formikValues: { + editedEvents: EditedEvents[] + }, + setConflicts: (conflicts: Conflict[]) => void, + dispatch: AppDispatch, ) => { - // Check if each start is before end - for (let i = 0; i < formikValues.editedEvents.length; i++) { - let event = formikValues.editedEvents[i]; - let startTime = new Date(); - startTime.setHours( - parseInt(event.changedStartTimeHour), - parseInt(event.changedStartTimeMinutes), - 0, - 0 - ); - let endTime = new Date(); - endTime.setHours( - parseInt(event.changedEndTimeHour), - parseInt(event.changedEndTimeMinutes), - 0, - 0 - ); + // Check if each start is before end + for (let i = 0; i < formikValues.editedEvents.length; i++) { + let event = formikValues.editedEvents[i]; + let startTime = new Date(); + startTime.setHours( + parseInt(event.changedStartTimeHour), + parseInt(event.changedStartTimeMinutes), + 0, + 0 + ); + let endTime = new Date(); + endTime.setHours( + parseInt(event.changedEndTimeHour), + parseInt(event.changedEndTimeMinutes), + 0, + 0 + ); - if (startTime > endTime) { - dispatch(addNotification({ - type: "error", - key: "CONFLICT_END_BEFORE_START", - duration: -1, - parameter: undefined, - context: NOTIFICATION_CONTEXT - })); - return false; - } - } + if (startTime > endTime) { + dispatch(addNotification({ + type: "error", + key: "CONFLICT_END_BEFORE_START", + duration: -1, + parameter: undefined, + context: NOTIFICATION_CONTEXT + })); + return false; + } + } - // Use backend for check for conflicts with other events - const response = await dispatch(checkForSchedulingConflicts(formikValues.editedEvents)); + // Use backend for check for conflicts with other events + const response = await dispatch(checkForSchedulingConflicts(formikValues.editedEvents)); - if (response.length > 0) { - setConflicts(response); - return false; - } else { - setConflicts([]); - return true; - } + if (response.length > 0) { + setConflicts(response); + return false; + } else { + setConflicts([]); + return true; + } }; // Check if an event is in a state that a task on it can be started export const isStartable = (event: Event) => { - return ( - event.event_status.toUpperCase().indexOf("PROCESSED") > -1 || - event.event_status.toUpperCase().indexOf("PROCESSING_FAILURE") > -1 || - event.event_status.toUpperCase().indexOf("PROCESSING_CANCELED") > -1 || - !event.selected - ); + return ( + event.event_status.toUpperCase().indexOf("PROCESSED") > -1 || + event.event_status.toUpperCase().indexOf("PROCESSING_FAILURE") > -1 || + event.event_status.toUpperCase().indexOf("PROCESSING_CANCELED") > -1 || + !event.selected + ); }; // Check if multiple events are in a state that a task on them can be started export const isTaskStartable = (events: Event[]) => { - for (let i = 0; i < events.length; i++) { - if (!isStartable(events[i])) { - return false; - } - } - return true; + for (let i = 0; i < events.length; i++) { + if (!isStartable(events[i])) { + return false; + } + } + return true; }; // Check validity of selected event list for bulk start task for activating next button export const checkValidityStartTaskEventSelection = (formikValues: { events: Event[] }) => { - if (formikValues.events.length > 0) { - if (isTaskStartable(formikValues.events)) { - return formikValues.events.some((event) => event.selected === true); - } else { - return false; - } - } else { - return false; - } + if (formikValues.events.length > 0) { + if (isTaskStartable(formikValues.events)) { + return formikValues.events.some((event) => event.selected === true); + } else { + return false; + } + } else { + return false; + } }; diff --git a/src/utils/componentStyles.ts b/src/utils/componentStyles.ts index 599d0c68e6..bce695ba8d 100644 --- a/src/utils/componentStyles.ts +++ b/src/utils/componentStyles.ts @@ -10,139 +10,139 @@ const colorDropDownDarkerFocus = "#2a62bc"; export function dropDownStyle(type: DropDownType): StylesConfig { - const width = - type === "theme" || type === "newTheme" || type === "workflow" - ? "100%" - : type === "time" - ? 70 - : type === "aclRole" - ? 360 - : type === "aclTemplate" || type === "comment" || type === "filter" - ? 200 - : 250; + const width = + type === "theme" || type === "newTheme" || type === "workflow" + ? "100%" + : type === "time" + ? 70 + : type === "aclRole" + ? 360 + : type === "aclTemplate" || type === "comment" || type === "filter" + ? 200 + : 250; - return { - container: (provided, state) => ({ - ...provided, - width: width, - position: "relative", - display: "inline-block", - verticalAlign: "middle", - font: "inherit", - paddingTop: 0, - paddingBottom: 0, - marginTop: 0, - marginBottom: 0, - marginRight: 1, - }), - control: (provided, state) => ({ - ...provided, - marginBottom: 0, - border: `1px solid ${colorDropDownMain}`, - borderColor: state.selectProps.menuIsOpen - ? colorDropDownNormalFocus - : colorDropDownMain, - hoverBorderColor: state.selectProps.menuIsOpen - ? colorDropDownNormalFocus - : colorDropDownMain, - boxShadow: state.selectProps.menuIsOpen - ? `0 0 0 1px ${colorDropDownNormalFocus}` - : `0 0 0 1px ${colorDropDownMain}`, - borderRadius: 4, - paddingTop: 0, - paddingBottom: 0, - "&:hover": { - borderColor: colorDropDownMain, - }, - }), - dropdownIndicator: (provided, state) => ({ - ...provided, - transform: state.selectProps.menuIsOpen - ? "rotate(180deg)" - : "rotate(0deg)", - paddingTop: 0, - paddingBottom: 0, - color: colorDropDownMain, - "&:hover": { - color: colorDropDownNormalFocus, - }, - }), - indicatorSeparator: (provided, state) => ({ - ...provided, - display: "none", - }), - input: (provided, state) => ({ - ...provided, - position: "relative", - zIndex: 1010, - margin: 0, - whiteSpace: "nowrap", - verticalAlign: "middle", - border: "none", - paddingTop: 0, - paddingBottom: 0, - }), - menu: (provided, state) => ({ - ...provided, - zIndex: 9000, - marginTop: 1, - border: "none", - }), - menuList: (provided, state) => ({ - ...provided, - marginTop: 0, - border: `1px solid ${colorDropDownMain}`, - borderRadius: 4, - }), - noOptionsMessage: (provided, state) => ({ - ...provided, - textAlign: "left", - paddingTop: 0, - paddingBottom: 0, - }), - option: (provided, state) => ({ - ...provided, - paddingTop: - type === "aclRole" || type === "aclTemplate" || type === "comment" || type === "filter" - ? 5 - : 0, - paddingBottom: - type === "aclRole" || type === "aclTemplate" || type === "comment" || type === "filter" - ? 5 - : 0, - backgroundColor: state.isSelected - ? colorDropDownDarkerFocus - : state.isFocused - ? colorDropDownNormalFocus - : "white", - color: state.isFocused || state.isSelected ? "white" : provided.color, - cursor: "pointer", - overflowWrap: "normal", - lineHeight: type === "comment" ? "105%" : "inherit", - }), - singleValue: (provided, state) => ({ - ...provided, - marginTop: 0, - marginBottom: 0, - paddingTop: 0, - paddingBottom: 0, - }), - valueContainer: (provided, state) => ({ - ...provided, - marginTop: 0, - marginBottom: 0, - paddingLeft: 5, - paddingTop: 0, - paddingBottom: 0, - }), - }; + return { + container: (provided, state) => ({ + ...provided, + width: width, + position: "relative", + display: "inline-block", + verticalAlign: "middle", + font: "inherit", + paddingTop: 0, + paddingBottom: 0, + marginTop: 0, + marginBottom: 0, + marginRight: 1, + }), + control: (provided, state) => ({ + ...provided, + marginBottom: 0, + border: `1px solid ${colorDropDownMain}`, + borderColor: state.selectProps.menuIsOpen + ? colorDropDownNormalFocus + : colorDropDownMain, + hoverBorderColor: state.selectProps.menuIsOpen + ? colorDropDownNormalFocus + : colorDropDownMain, + boxShadow: state.selectProps.menuIsOpen + ? `0 0 0 1px ${colorDropDownNormalFocus}` + : `0 0 0 1px ${colorDropDownMain}`, + borderRadius: 4, + paddingTop: 0, + paddingBottom: 0, + "&:hover": { + borderColor: colorDropDownMain, + }, + }), + dropdownIndicator: (provided, state) => ({ + ...provided, + transform: state.selectProps.menuIsOpen + ? "rotate(180deg)" + : "rotate(0deg)", + paddingTop: 0, + paddingBottom: 0, + color: colorDropDownMain, + "&:hover": { + color: colorDropDownNormalFocus, + }, + }), + indicatorSeparator: (provided, state) => ({ + ...provided, + display: "none", + }), + input: (provided, state) => ({ + ...provided, + position: "relative", + zIndex: 1010, + margin: 0, + whiteSpace: "nowrap", + verticalAlign: "middle", + border: "none", + paddingTop: 0, + paddingBottom: 0, + }), + menu: (provided, state) => ({ + ...provided, + zIndex: 9000, + marginTop: 1, + border: "none", + }), + menuList: (provided, state) => ({ + ...provided, + marginTop: 0, + border: `1px solid ${colorDropDownMain}`, + borderRadius: 4, + }), + noOptionsMessage: (provided, state) => ({ + ...provided, + textAlign: "left", + paddingTop: 0, + paddingBottom: 0, + }), + option: (provided, state) => ({ + ...provided, + paddingTop: + type === "aclRole" || type === "aclTemplate" || type === "comment" || type === "filter" + ? 5 + : 0, + paddingBottom: + type === "aclRole" || type === "aclTemplate" || type === "comment" || type === "filter" + ? 5 + : 0, + backgroundColor: state.isSelected + ? colorDropDownDarkerFocus + : state.isFocused + ? colorDropDownNormalFocus + : "white", + color: state.isFocused || state.isSelected ? "white" : provided.color, + cursor: "pointer", + overflowWrap: "normal", + lineHeight: type === "comment" ? "105%" : "inherit", + }), + singleValue: (provided, state) => ({ + ...provided, + marginTop: 0, + marginBottom: 0, + paddingTop: 0, + paddingBottom: 0, + }), + valueContainer: (provided, state) => ({ + ...provided, + marginTop: 0, + marginBottom: 0, + paddingLeft: 5, + paddingTop: 0, + paddingBottom: 0, + }), + }; }; export const dropDownSpacingTheme = (theme: Theme) => ({ - ...theme, - spacing: { - ...theme.spacing, - controlHeight: 25, - baseUnit: 2, - }, + ...theme, + spacing: { + ...theme.spacing, + controlHeight: 25, + baseUnit: 2, + }, }); diff --git a/src/utils/componentsUtils.ts b/src/utils/componentsUtils.ts index 06794a9f8c..771a448316 100644 --- a/src/utils/componentsUtils.ts +++ b/src/utils/componentsUtils.ts @@ -1,6 +1,6 @@ export const styleNavOpen = { - marginLeft: "130px", + marginLeft: "130px", }; export const styleNavClosed = { - marginLeft: "20px", + marginLeft: "20px", }; diff --git a/src/utils/dateUtils.ts b/src/utils/dateUtils.ts index a110db5646..0ce1cad19b 100644 --- a/src/utils/dateUtils.ts +++ b/src/utils/dateUtils.ts @@ -8,728 +8,728 @@ import { FormikErrors } from "formik"; // check if date can be parsed export const renderValidDate = (date: string) => { - return !isNaN(Date.parse(date)) ? new Date(date) : "" + return !isNaN(Date.parse(date)) ? new Date(date) : "" } // transform relative date to an absolute date export const relativeToAbsoluteDate = (relative: string, type: string, from: boolean) => { - let localMoment = moment(); + let localMoment = moment(); - let absolute; - if (from) { - absolute = localMoment.startOf(type as moment.unitOfTime.StartOf); - } else { - absolute = localMoment.endOf(type as moment.unitOfTime.StartOf); - } + let absolute; + if (from) { + absolute = localMoment.startOf(type as moment.unitOfTime.StartOf); + } else { + absolute = localMoment.endOf(type as moment.unitOfTime.StartOf); + } - absolute = absolute.add(relative, type as moment.unitOfTime.Base); + absolute = absolute.add(relative, type as moment.unitOfTime.Base); - return absolute.toDate(); + return absolute.toDate(); }; // transform from relative date span to filter value containing absolute dates export const relativeDateSpanToFilterValue = ( - fromRelativeDate: string, - toRelativeDate: string, - type: string + fromRelativeDate: string, + toRelativeDate: string, + type: string ) => { - let fromAbsoluteDate = relativeToAbsoluteDate(fromRelativeDate, type, true); - let toAbsoluteDate = relativeToAbsoluteDate(toRelativeDate, type, false); - - return ( - fromAbsoluteDate.toISOString() + - "/" + - toAbsoluteDate.toISOString() - ).toString(); + let fromAbsoluteDate = relativeToAbsoluteDate(fromRelativeDate, type, true); + let toAbsoluteDate = relativeToAbsoluteDate(toRelativeDate, type, false); + + return ( + fromAbsoluteDate.toISOString() + + "/" + + toAbsoluteDate.toISOString() + ).toString(); }; // creates a date object from a date, hour and minute export const makeDate = ( - date: string | number | Date, - hour: string, - minute: string + date: string | number | Date, + hour: string, + minute: string ) => { - const madeDate = new Date(date); - madeDate.setHours(parseInt(hour)); - madeDate.setMinutes(parseInt(minute)); + const madeDate = new Date(date); + madeDate.setHours(parseInt(hour)); + madeDate.setMinutes(parseInt(minute)); - return madeDate; + return madeDate; }; // calculates the duration between a start and end date in hours and minutes export const calculateDuration = ( - startDate: Date, - endDate: Date + startDate: Date, + endDate: Date ) => { - const duration = (endDate.getTime() - startDate.getTime()) / 1000; - const durationHours = (duration - (duration % 3600)) / 3600; - const durationMinutes = (duration % 3600) / 60; + const duration = (endDate.getTime() - startDate.getTime()) / 1000; + const durationHours = (duration - (duration % 3600)) / 3600; + const durationMinutes = (duration % 3600) / 60; - return { durationHours, durationMinutes }; + return { durationHours, durationMinutes }; }; // sets the duration in the formik const setDuration = ( - startDate: Date, - endDate: Date , - setFieldValue: (field: string, value: string) => Promise> + startDate: Date, + endDate: Date , + setFieldValue: (field: string, value: string) => Promise> ) => { - const { durationHours, durationMinutes } = calculateDuration( - startDate, - endDate - ); + const { durationHours, durationMinutes } = calculateDuration( + startDate, + endDate + ); - setFieldValue("scheduleDurationHours", makeTwoDigits(durationHours)); - setFieldValue("scheduleDurationMinutes", makeTwoDigits(durationMinutes)); + setFieldValue("scheduleDurationHours", makeTwoDigits(durationHours)); + setFieldValue("scheduleDurationMinutes", makeTwoDigits(durationMinutes)); }; // checks if the time of the endDate is before the time of the startDate const isEndBeforeStart = ( - startDate: Date, - endDate: Date + startDate: Date, + endDate: Date ) => { - return startDate > endDate; + return startDate > endDate; }; type RequiredFormikValues = { - scheduleEndHour: string, - scheduleEndMinute: string, - captureAgent?: string, // Ideally this should be required if "checkConflicts" is not undefined + scheduleEndHour: string, + scheduleEndMinute: string, + captureAgent?: string, // Ideally this should be required if "checkConflicts" is not undefined } // changes the start in the formik const changeStart = ( - eventId: string, - start: { - date: string | number | Date, - hour: string, - minute: string, - }, - formikValues: RequiredFormikValues, - setFieldValue: (field: string, value: string) => Promise>, - checkConflicts?: (id: string, startDate: Date, endDate: Date, ca: string) => unknown, + eventId: string, + start: { + date: string | number | Date, + hour: string, + minute: string, + }, + formikValues: RequiredFormikValues, + setFieldValue: (field: string, value: string) => Promise>, + checkConflicts?: (id: string, startDate: Date, endDate: Date, ca: string) => unknown, ) => { - const startDate = makeDate(start.date, start.hour, start.minute); - let endDate = makeDate( - start.date, - formikValues.scheduleEndHour, - formikValues.scheduleEndMinute - ); - - if (isEndBeforeStart(startDate, endDate)) { - endDate.setDate(startDate.getDate() + 1); - } - - setDuration(startDate, endDate, setFieldValue); - setFieldValue("scheduleEndDate", endDate.toISOString()); - setFieldValue("scheduleStartDate", startDate.toISOString()); - - if (!!checkConflicts && !!formikValues.captureAgent) { - checkConflicts( - eventId, - startDate, - endDate, - formikValues.captureAgent - ); - } + const startDate = makeDate(start.date, start.hour, start.minute); + let endDate = makeDate( + start.date, + formikValues.scheduleEndHour, + formikValues.scheduleEndMinute + ); + + if (isEndBeforeStart(startDate, endDate)) { + endDate.setDate(startDate.getDate() + 1); + } + + setDuration(startDate, endDate, setFieldValue); + setFieldValue("scheduleEndDate", endDate.toISOString()); + setFieldValue("scheduleStartDate", startDate.toISOString()); + + if (!!checkConflicts && !!formikValues.captureAgent) { + checkConflicts( + eventId, + startDate, + endDate, + formikValues.captureAgent + ); + } }; export const changeStartDate = ( - value: Date, - formikValues: RequiredFormikValues & { scheduleStartHour: string, scheduleStartMinute: string }, - setFieldValue: (field: string, value: string) => Promise>, - eventId = "", - checkConflicts?: (id: string, startDate: Date, endDate: Date, ca: string) => unknown + value: Date, + formikValues: RequiredFormikValues & { scheduleStartHour: string, scheduleStartMinute: string }, + setFieldValue: (field: string, value: string) => Promise>, + eventId = "", + checkConflicts?: (id: string, startDate: Date, endDate: Date, ca: string) => unknown ) => { - changeStart( - eventId, - { - date: value, - hour: formikValues.scheduleStartHour, - minute: formikValues.scheduleStartMinute, - }, - formikValues, - setFieldValue, - checkConflicts - ); + changeStart( + eventId, + { + date: value, + hour: formikValues.scheduleStartHour, + minute: formikValues.scheduleStartMinute, + }, + formikValues, + setFieldValue, + checkConflicts + ); }; export const changeStartHour = async ( - value: string, - formikValues: RequiredFormikValues & { scheduleStartDate: string | number, scheduleStartMinute: string }, - setFieldValue: (field: string, value: string) => Promise>, - eventId = "", - checkConflicts?: (id: string, startDate: Date, endDate: Date, ca: string) => unknown + value: string, + formikValues: RequiredFormikValues & { scheduleStartDate: string | number, scheduleStartMinute: string }, + setFieldValue: (field: string, value: string) => Promise>, + eventId = "", + checkConflicts?: (id: string, startDate: Date, endDate: Date, ca: string) => unknown ) => { - changeStart( - eventId, - { - date: formikValues.scheduleStartDate, - hour: value, - minute: formikValues.scheduleStartMinute, - }, - formikValues, - setFieldValue, - checkConflicts - ); - - setFieldValue("scheduleStartHour", value); + changeStart( + eventId, + { + date: formikValues.scheduleStartDate, + hour: value, + minute: formikValues.scheduleStartMinute, + }, + formikValues, + setFieldValue, + checkConflicts + ); + + setFieldValue("scheduleStartHour", value); }; export const changeStartMinute = async ( - value: string, - formikValues: RequiredFormikValues & { scheduleStartDate: string | number, scheduleStartHour: string }, - setFieldValue: (field: string, value: string) => Promise>, - eventId = "", - checkConflicts?: (id: string, startDate: Date, endDate: Date, ca: string) => unknown + value: string, + formikValues: RequiredFormikValues & { scheduleStartDate: string | number, scheduleStartHour: string }, + setFieldValue: (field: string, value: string) => Promise>, + eventId = "", + checkConflicts?: (id: string, startDate: Date, endDate: Date, ca: string) => unknown ) => { - changeStart( - eventId, - { - date: formikValues.scheduleStartDate, - hour: formikValues.scheduleStartHour, - minute: value, - }, - formikValues, - setFieldValue, - checkConflicts - ); - - setFieldValue("scheduleStartMinute", value); + changeStart( + eventId, + { + date: formikValues.scheduleStartDate, + hour: formikValues.scheduleStartHour, + minute: value, + }, + formikValues, + setFieldValue, + checkConflicts + ); + + setFieldValue("scheduleStartMinute", value); }; // changes the end in the formik const changeEnd = ( - eventId: string, - end: { - hour: string, - minute: string, - }, - formikValues: RequiredFormikValues & { scheduleStartDate: string | number, scheduleStartHour: string, scheduleStartMinute: string }, - setFieldValue: (field: string, value: string) => Promise>, - checkConflicts?: (id: string, startDate: Date, endDate: Date, ca: string) => unknown + eventId: string, + end: { + hour: string, + minute: string, + }, + formikValues: RequiredFormikValues & { scheduleStartDate: string | number, scheduleStartHour: string, scheduleStartMinute: string }, + setFieldValue: (field: string, value: string) => Promise>, + checkConflicts?: (id: string, startDate: Date, endDate: Date, ca: string) => unknown ) => { - const endDate = makeDate( - formikValues.scheduleStartDate, - end.hour, - end.minute - ); - const startDate = makeDate( - formikValues.scheduleStartDate, - formikValues.scheduleStartHour, - formikValues.scheduleStartMinute - ); - - if (isEndBeforeStart(startDate, endDate)) { - endDate.setDate(startDate.getDate() + 1); - } - - setDuration(startDate, endDate, setFieldValue); - setFieldValue("scheduleEndDate", endDate.toISOString()); - - if (!!checkConflicts && !!formikValues.captureAgent) { - checkConflicts( - eventId, - startDate, - endDate, - formikValues.captureAgent - ); - } + const endDate = makeDate( + formikValues.scheduleStartDate, + end.hour, + end.minute + ); + const startDate = makeDate( + formikValues.scheduleStartDate, + formikValues.scheduleStartHour, + formikValues.scheduleStartMinute + ); + + if (isEndBeforeStart(startDate, endDate)) { + endDate.setDate(startDate.getDate() + 1); + } + + setDuration(startDate, endDate, setFieldValue); + setFieldValue("scheduleEndDate", endDate.toISOString()); + + if (!!checkConflicts && !!formikValues.captureAgent) { + checkConflicts( + eventId, + startDate, + endDate, + formikValues.captureAgent + ); + } }; export const changeEndHour = async ( - value: string, - formikValues: RequiredFormikValues & { scheduleStartDate: string | number, scheduleStartHour: string, scheduleStartMinute: string }, - setFieldValue: (field: string, value: string) => Promise>, - eventId = "", - checkConflicts?: (id: string, startDate: Date, endDate: Date, ca: string) => unknown + value: string, + formikValues: RequiredFormikValues & { scheduleStartDate: string | number, scheduleStartHour: string, scheduleStartMinute: string }, + setFieldValue: (field: string, value: string) => Promise>, + eventId = "", + checkConflicts?: (id: string, startDate: Date, endDate: Date, ca: string) => unknown ) => { - changeEnd( - eventId, - { - hour: value, - minute: formikValues.scheduleEndMinute, - }, - formikValues, - setFieldValue, - checkConflicts - ); - - setFieldValue("scheduleEndHour", value); + changeEnd( + eventId, + { + hour: value, + minute: formikValues.scheduleEndMinute, + }, + formikValues, + setFieldValue, + checkConflicts + ); + + setFieldValue("scheduleEndHour", value); }; export const changeEndMinute = async ( - value: string, - formikValues: RequiredFormikValues & { scheduleStartDate: string | number, scheduleStartHour: string, scheduleStartMinute: string }, - setFieldValue: (field: string, value: string) => Promise>, - eventId = "", - checkConflicts?: (id: string, startDate: Date, endDate: Date, ca: string) => unknown + value: string, + formikValues: RequiredFormikValues & { scheduleStartDate: string | number, scheduleStartHour: string, scheduleStartMinute: string }, + setFieldValue: (field: string, value: string) => Promise>, + eventId = "", + checkConflicts?: (id: string, startDate: Date, endDate: Date, ca: string) => unknown ) => { - changeEnd( - eventId, - { - hour: formikValues.scheduleEndHour, - minute: value, - }, - formikValues, - setFieldValue, - checkConflicts - ); - - setFieldValue("scheduleEndMinute", value); + changeEnd( + eventId, + { + hour: formikValues.scheduleEndHour, + minute: value, + }, + formikValues, + setFieldValue, + checkConflicts + ); + + setFieldValue("scheduleEndMinute", value); }; // changes the duration in the formik const changeDuration = ( - eventId: string, - duration: { hours: string, minutes: string }, - formikValues: RequiredFormikValues & { scheduleStartDate: string | number, scheduleStartHour: string, scheduleStartMinute: string }, - setFieldValue: (field: string, value: string) => Promise>, - checkConflicts?: (id: string, startDate: Date, endDate: Date, ca: string) => unknown + eventId: string, + duration: { hours: string, minutes: string }, + formikValues: RequiredFormikValues & { scheduleStartDate: string | number, scheduleStartHour: string, scheduleStartMinute: string }, + setFieldValue: (field: string, value: string) => Promise>, + checkConflicts?: (id: string, startDate: Date, endDate: Date, ca: string) => unknown ) => { - const startDate = makeDate( - formikValues.scheduleStartDate, - formikValues.scheduleStartHour, - formikValues.scheduleStartMinute, - ); - const endDate = new Date(startDate.toISOString()); - - endDate.setHours(endDate.getHours() + parseInt(duration.hours)); - endDate.setMinutes(endDate.getMinutes() + parseInt(duration.minutes)); - - setFieldValue("scheduleEndHour", makeTwoDigits(endDate.getHours())); - setFieldValue("scheduleEndMinute", makeTwoDigits(endDate.getMinutes())); - setFieldValue("scheduleEndDate", endDate.toISOString()); - - if (!!checkConflicts && !!formikValues.captureAgent) { - checkConflicts( - eventId, - startDate, - endDate, - formikValues.captureAgent - ); - } + const startDate = makeDate( + formikValues.scheduleStartDate, + formikValues.scheduleStartHour, + formikValues.scheduleStartMinute, + ); + const endDate = new Date(startDate.toISOString()); + + endDate.setHours(endDate.getHours() + parseInt(duration.hours)); + endDate.setMinutes(endDate.getMinutes() + parseInt(duration.minutes)); + + setFieldValue("scheduleEndHour", makeTwoDigits(endDate.getHours())); + setFieldValue("scheduleEndMinute", makeTwoDigits(endDate.getMinutes())); + setFieldValue("scheduleEndDate", endDate.toISOString()); + + if (!!checkConflicts && !!formikValues.captureAgent) { + checkConflicts( + eventId, + startDate, + endDate, + formikValues.captureAgent + ); + } }; export const changeDurationHour = async ( - value: string, - formikValues: RequiredFormikValues & { - scheduleStartDate: string | number, - scheduleStartHour: string, - scheduleStartMinute: string, - scheduleDurationMinutes: string - }, - setFieldValue: (field: string, value: string) => Promise>, - eventId = "", - checkConflicts?: (id: string, startDate: Date, endDate: Date, ca: string) => unknown + value: string, + formikValues: RequiredFormikValues & { + scheduleStartDate: string | number, + scheduleStartHour: string, + scheduleStartMinute: string, + scheduleDurationMinutes: string + }, + setFieldValue: (field: string, value: string) => Promise>, + eventId = "", + checkConflicts?: (id: string, startDate: Date, endDate: Date, ca: string) => unknown ) => { - changeDuration( - eventId, - { - hours: value, - minutes: formikValues.scheduleDurationMinutes, - }, - formikValues, - setFieldValue, - checkConflicts - ); - - setFieldValue("scheduleDurationHours", value); + changeDuration( + eventId, + { + hours: value, + minutes: formikValues.scheduleDurationMinutes, + }, + formikValues, + setFieldValue, + checkConflicts + ); + + setFieldValue("scheduleDurationHours", value); }; export const changeDurationMinute = async ( - value: string, - formikValues: RequiredFormikValues & { - scheduleStartDate: string | number, - scheduleStartHour: string, - scheduleStartMinute: string, - scheduleDurationHours: string - }, - setFieldValue: (field: string, value: string) => Promise>, - eventId = "", - checkConflicts?: (id: string, startDate: Date, endDate: Date, ca: string) => unknown + value: string, + formikValues: RequiredFormikValues & { + scheduleStartDate: string | number, + scheduleStartHour: string, + scheduleStartMinute: string, + scheduleDurationHours: string + }, + setFieldValue: (field: string, value: string) => Promise>, + eventId = "", + checkConflicts?: (id: string, startDate: Date, endDate: Date, ca: string) => unknown ) => { - changeDuration( - eventId, - { - hours: formikValues.scheduleDurationHours, - minutes: value, - }, - formikValues, - setFieldValue, - checkConflicts - ); - - setFieldValue("scheduleDurationMinutes", value); + changeDuration( + eventId, + { + hours: formikValues.scheduleDurationHours, + minutes: value, + }, + formikValues, + setFieldValue, + checkConflicts + ); + + setFieldValue("scheduleDurationMinutes", value); }; // changes the start in the formik const changeStartMultiple = ( - eventId: string, - start: { - date: string | number | Date, - hour: string, - minute: string, - }, - formikValues: RequiredFormikValues & { - scheduleEndDate: string, - }, - setFieldValue: (field: string, value: string) => Promise>, - checkConflicts?: (id: string, startDate: Date, endDate: Date, ca: string) => unknown + eventId: string, + start: { + date: string | number | Date, + hour: string, + minute: string, + }, + formikValues: RequiredFormikValues & { + scheduleEndDate: string, + }, + setFieldValue: (field: string, value: string) => Promise>, + checkConflicts?: (id: string, startDate: Date, endDate: Date, ca: string) => unknown ) => { - const startDate = makeDate(start.date, start.hour, start.minute); - let endDate = makeDate( - start.date, - formikValues.scheduleEndHour, - formikValues.scheduleEndMinute - ); - - if (isEndBeforeStart(startDate, endDate)) { - endDate.setDate(startDate.getDate() + 1); - } - - setDuration(startDate, endDate, setFieldValue); - - endDate = makeDate( - formikValues.scheduleEndDate, - formikValues.scheduleEndHour, - formikValues.scheduleEndMinute - ); - - if (isEndBeforeStart(startDate, endDate)) { - endDate.setDate(startDate.getDate() + 1); - } - - setFieldValue("scheduleEndDate", endDate.toISOString()); - setFieldValue("scheduleStartDate", startDate.toISOString()); - - if (!!checkConflicts && !! formikValues.captureAgent) { - checkConflicts( - eventId, - startDate, - endDate, - formikValues.captureAgent - ); - } + const startDate = makeDate(start.date, start.hour, start.minute); + let endDate = makeDate( + start.date, + formikValues.scheduleEndHour, + formikValues.scheduleEndMinute + ); + + if (isEndBeforeStart(startDate, endDate)) { + endDate.setDate(startDate.getDate() + 1); + } + + setDuration(startDate, endDate, setFieldValue); + + endDate = makeDate( + formikValues.scheduleEndDate, + formikValues.scheduleEndHour, + formikValues.scheduleEndMinute + ); + + if (isEndBeforeStart(startDate, endDate)) { + endDate.setDate(startDate.getDate() + 1); + } + + setFieldValue("scheduleEndDate", endDate.toISOString()); + setFieldValue("scheduleStartDate", startDate.toISOString()); + + if (!!checkConflicts && !! formikValues.captureAgent) { + checkConflicts( + eventId, + startDate, + endDate, + formikValues.captureAgent + ); + } }; export const changeStartDateMultiple = ( - value: Date, - formikValues: RequiredFormikValues & { - scheduleEndDate: string, - scheduleStartHour: string, - scheduleStartMinute: string, - }, - setFieldValue: (field: string, value: string) => Promise>, - eventId = "", - checkConflicts?: (id: string, startDate: Date, endDate: Date, ca: string) => unknown + value: Date, + formikValues: RequiredFormikValues & { + scheduleEndDate: string, + scheduleStartHour: string, + scheduleStartMinute: string, + }, + setFieldValue: (field: string, value: string) => Promise>, + eventId = "", + checkConflicts?: (id: string, startDate: Date, endDate: Date, ca: string) => unknown ) => { - changeStartMultiple( - eventId, - { - date: value, - hour: formikValues.scheduleStartHour, - minute: formikValues.scheduleStartMinute, - }, - formikValues, - setFieldValue, - checkConflicts - ); + changeStartMultiple( + eventId, + { + date: value, + hour: formikValues.scheduleStartHour, + minute: formikValues.scheduleStartMinute, + }, + formikValues, + setFieldValue, + checkConflicts + ); }; export const changeStartHourMultiple = async ( - value: string, - formikValues: RequiredFormikValues & { - scheduleEndDate: string, - scheduleStartDate: string, - scheduleStartMinute: string, - }, - setFieldValue: (field: string, value: string) => Promise>, - eventId = "", - checkConflicts?: (id: string, startDate: Date, endDate: Date, ca: string) => unknown + value: string, + formikValues: RequiredFormikValues & { + scheduleEndDate: string, + scheduleStartDate: string, + scheduleStartMinute: string, + }, + setFieldValue: (field: string, value: string) => Promise>, + eventId = "", + checkConflicts?: (id: string, startDate: Date, endDate: Date, ca: string) => unknown ) => { - changeStartMultiple( - eventId, - { - date: formikValues.scheduleStartDate, - hour: value, - minute: formikValues.scheduleStartMinute, - }, - formikValues, - setFieldValue, - checkConflicts - ); - - setFieldValue("scheduleStartHour", value); + changeStartMultiple( + eventId, + { + date: formikValues.scheduleStartDate, + hour: value, + minute: formikValues.scheduleStartMinute, + }, + formikValues, + setFieldValue, + checkConflicts + ); + + setFieldValue("scheduleStartHour", value); }; export const changeStartMinuteMultiple = async ( - value: string, - formikValues: RequiredFormikValues & { - scheduleEndDate: string, - scheduleStartDate: string, - scheduleStartHour: string, - }, - setFieldValue: (field: string, value: string) => Promise>, - eventId = "", - checkConflicts?: (id: string, startDate: Date, endDate: Date, ca: string) => unknown + value: string, + formikValues: RequiredFormikValues & { + scheduleEndDate: string, + scheduleStartDate: string, + scheduleStartHour: string, + }, + setFieldValue: (field: string, value: string) => Promise>, + eventId = "", + checkConflicts?: (id: string, startDate: Date, endDate: Date, ca: string) => unknown ) => { - changeStartMultiple( - eventId, - { - date: formikValues.scheduleStartDate, - hour: formikValues.scheduleStartHour, - minute: value, - }, - formikValues, - setFieldValue, - checkConflicts - ); - - setFieldValue("scheduleStartMinute", value); + changeStartMultiple( + eventId, + { + date: formikValues.scheduleStartDate, + hour: formikValues.scheduleStartHour, + minute: value, + }, + formikValues, + setFieldValue, + checkConflicts + ); + + setFieldValue("scheduleStartMinute", value); }; // changes the end in the formik export const changeEndDateMultiple = async ( - value: string | Date, - formikValues: RequiredFormikValues & { - scheduleEndDate: string, - scheduleStartDate: string, - scheduleStartHour: string, - scheduleStartMinute: string, - }, - setFieldValue: (field: string, value: string) => Promise>, - eventId = "", - checkConflicts?: (id: string, startDate: Date, endDate: Date, ca: string) => unknown + value: string | Date, + formikValues: RequiredFormikValues & { + scheduleEndDate: string, + scheduleStartDate: string, + scheduleStartHour: string, + scheduleStartMinute: string, + }, + setFieldValue: (field: string, value: string) => Promise>, + eventId = "", + checkConflicts?: (id: string, startDate: Date, endDate: Date, ca: string) => unknown ) => { - const endDate = makeDate( - value, - formikValues.scheduleEndHour, - formikValues.scheduleEndMinute - ); - const startDate = makeDate( - formikValues.scheduleStartDate, - formikValues.scheduleStartHour, - formikValues.scheduleStartMinute - ); - - if (isEndBeforeStart(startDate, endDate)) { - startDate.setDate(endDate.getDate()); - if (isEndBeforeStart(startDate, endDate)) { - startDate.setDate(endDate.getDate() - 1); - } - } - - setFieldValue("scheduleEndDate", endDate.toISOString()); - setFieldValue("scheduleStartDate", startDate.toISOString()); - - if (!!checkConflicts && !!formikValues.captureAgent) { - checkConflicts( - eventId, - startDate, - endDate, - formikValues.captureAgent - ); - } + const endDate = makeDate( + value, + formikValues.scheduleEndHour, + formikValues.scheduleEndMinute + ); + const startDate = makeDate( + formikValues.scheduleStartDate, + formikValues.scheduleStartHour, + formikValues.scheduleStartMinute + ); + + if (isEndBeforeStart(startDate, endDate)) { + startDate.setDate(endDate.getDate()); + if (isEndBeforeStart(startDate, endDate)) { + startDate.setDate(endDate.getDate() - 1); + } + } + + setFieldValue("scheduleEndDate", endDate.toISOString()); + setFieldValue("scheduleStartDate", startDate.toISOString()); + + if (!!checkConflicts && !!formikValues.captureAgent) { + checkConflicts( + eventId, + startDate, + endDate, + formikValues.captureAgent + ); + } }; // changes the end in the formik const changeEndMultiple = ( - eventId: string, - end: { - hour: string, - minute: string, - }, - formikValues: RequiredFormikValues & { - scheduleEndDate: string, - scheduleStartDate: string, - scheduleStartHour: string, - scheduleStartMinute: string, - }, - setFieldValue: (field: string, value: string) => Promise>, - checkConflicts?: (id: string, startDate: Date, endDate: Date, ca: string) => unknown + eventId: string, + end: { + hour: string, + minute: string, + }, + formikValues: RequiredFormikValues & { + scheduleEndDate: string, + scheduleStartDate: string, + scheduleStartHour: string, + scheduleStartMinute: string, + }, + setFieldValue: (field: string, value: string) => Promise>, + checkConflicts?: (id: string, startDate: Date, endDate: Date, ca: string) => unknown ) => { - let endDate = makeDate(formikValues.scheduleStartDate, end.hour, end.minute); - const startDate = makeDate( - formikValues.scheduleStartDate, - formikValues.scheduleStartHour, - formikValues.scheduleStartMinute - ); - - if (isEndBeforeStart(startDate, endDate)) { - endDate.setDate(startDate.getDate() + 1); - } - - setDuration(startDate, endDate, setFieldValue); - - endDate = makeDate(formikValues.scheduleEndDate, end.hour, end.minute); - - if (isEndBeforeStart(startDate, endDate)) { - endDate.setDate(startDate.getDate() + 1); - setFieldValue("scheduleEndDate", endDate.toISOString()); - } - - if (!!checkConflicts && !!formikValues.captureAgent) { - checkConflicts( - eventId, - startDate, - endDate, - formikValues.captureAgent - ); - } + let endDate = makeDate(formikValues.scheduleStartDate, end.hour, end.minute); + const startDate = makeDate( + formikValues.scheduleStartDate, + formikValues.scheduleStartHour, + formikValues.scheduleStartMinute + ); + + if (isEndBeforeStart(startDate, endDate)) { + endDate.setDate(startDate.getDate() + 1); + } + + setDuration(startDate, endDate, setFieldValue); + + endDate = makeDate(formikValues.scheduleEndDate, end.hour, end.minute); + + if (isEndBeforeStart(startDate, endDate)) { + endDate.setDate(startDate.getDate() + 1); + setFieldValue("scheduleEndDate", endDate.toISOString()); + } + + if (!!checkConflicts && !!formikValues.captureAgent) { + checkConflicts( + eventId, + startDate, + endDate, + formikValues.captureAgent + ); + } }; export const changeEndHourMultiple = async ( - value: string, - formikValues: RequiredFormikValues & { - scheduleEndDate: string, - scheduleStartDate: string, - scheduleStartHour: string, - scheduleStartMinute: string, - }, - setFieldValue: (field: string, value: string) => Promise>, - eventId = "", - checkConflicts?: (id: string, startDate: Date, endDate: Date, ca: string) => unknown + value: string, + formikValues: RequiredFormikValues & { + scheduleEndDate: string, + scheduleStartDate: string, + scheduleStartHour: string, + scheduleStartMinute: string, + }, + setFieldValue: (field: string, value: string) => Promise>, + eventId = "", + checkConflicts?: (id: string, startDate: Date, endDate: Date, ca: string) => unknown ) => { - changeEndMultiple( - eventId, - { - hour: value, - minute: formikValues.scheduleEndMinute, - }, - formikValues, - setFieldValue, - checkConflicts - ); - - setFieldValue("scheduleEndHour", value); + changeEndMultiple( + eventId, + { + hour: value, + minute: formikValues.scheduleEndMinute, + }, + formikValues, + setFieldValue, + checkConflicts + ); + + setFieldValue("scheduleEndHour", value); }; export const changeEndMinuteMultiple = async ( - value: string, - formikValues: RequiredFormikValues & { - scheduleEndDate: string, - scheduleStartDate: string, - scheduleStartHour: string, - scheduleStartMinute: string, - }, - setFieldValue: (field: string, value: string) => Promise>, - eventId = "", - checkConflicts?: (id: string, startDate: Date, endDate: Date, ca: string) => unknown + value: string, + formikValues: RequiredFormikValues & { + scheduleEndDate: string, + scheduleStartDate: string, + scheduleStartHour: string, + scheduleStartMinute: string, + }, + setFieldValue: (field: string, value: string) => Promise>, + eventId = "", + checkConflicts?: (id: string, startDate: Date, endDate: Date, ca: string) => unknown ) => { - changeEndMultiple( - eventId, - { - hour: formikValues.scheduleEndHour, - minute: value, - }, - formikValues, - setFieldValue, - checkConflicts - ); - - setFieldValue("scheduleEndMinute", value); + changeEndMultiple( + eventId, + { + hour: formikValues.scheduleEndHour, + minute: value, + }, + formikValues, + setFieldValue, + checkConflicts + ); + + setFieldValue("scheduleEndMinute", value); }; // changes the duration in the formik const changeDurationMultiple = ( - eventId: string, - duration: { - hours: string, - minutes: string, - }, - formikValues: RequiredFormikValues & { - scheduleEndDate: string, - scheduleStartDate: string, - scheduleStartHour: string, - scheduleStartMinute: string, - }, - setFieldValue: (field: string, value: string) => Promise>, - checkConflicts?: (id: string, startDate: Date, endDate: Date, ca: string) => unknown + eventId: string, + duration: { + hours: string, + minutes: string, + }, + formikValues: RequiredFormikValues & { + scheduleEndDate: string, + scheduleStartDate: string, + scheduleStartHour: string, + scheduleStartMinute: string, + }, + setFieldValue: (field: string, value: string) => Promise>, + checkConflicts?: (id: string, startDate: Date, endDate: Date, ca: string) => unknown ) => { - const startDate = makeDate( - formikValues.scheduleStartDate, - formikValues.scheduleStartHour, - formikValues.scheduleStartMinute - ); - const endDate = makeDate( - formikValues.scheduleEndDate, - formikValues.scheduleStartHour, - formikValues.scheduleStartMinute - ); - - endDate.setHours(endDate.getHours() + parseInt(duration.hours)); - endDate.setMinutes(endDate.getMinutes() + parseInt(duration.minutes)); - - setFieldValue("scheduleEndHour", makeTwoDigits(endDate.getHours())); - setFieldValue("scheduleEndMinute", makeTwoDigits(endDate.getMinutes())); - setFieldValue("scheduleEndDate", endDate.toISOString()); - - if (!!checkConflicts && !!formikValues.captureAgent) { - checkConflicts( - eventId, - startDate, - endDate, - formikValues.captureAgent - ); - } + const startDate = makeDate( + formikValues.scheduleStartDate, + formikValues.scheduleStartHour, + formikValues.scheduleStartMinute + ); + const endDate = makeDate( + formikValues.scheduleEndDate, + formikValues.scheduleStartHour, + formikValues.scheduleStartMinute + ); + + endDate.setHours(endDate.getHours() + parseInt(duration.hours)); + endDate.setMinutes(endDate.getMinutes() + parseInt(duration.minutes)); + + setFieldValue("scheduleEndHour", makeTwoDigits(endDate.getHours())); + setFieldValue("scheduleEndMinute", makeTwoDigits(endDate.getMinutes())); + setFieldValue("scheduleEndDate", endDate.toISOString()); + + if (!!checkConflicts && !!formikValues.captureAgent) { + checkConflicts( + eventId, + startDate, + endDate, + formikValues.captureAgent + ); + } }; export const changeDurationHourMultiple = async ( - value: string, - formikValues: RequiredFormikValues & { - scheduleEndDate: string, - scheduleStartDate: string, - scheduleStartHour: string, - scheduleStartMinute: string, - scheduleDurationMinutes: string, - }, - setFieldValue: (field: string, value: string) => Promise>, - eventId = "", - checkConflicts?: (id: string, startDate: Date, endDate: Date, ca: string) => unknown + value: string, + formikValues: RequiredFormikValues & { + scheduleEndDate: string, + scheduleStartDate: string, + scheduleStartHour: string, + scheduleStartMinute: string, + scheduleDurationMinutes: string, + }, + setFieldValue: (field: string, value: string) => Promise>, + eventId = "", + checkConflicts?: (id: string, startDate: Date, endDate: Date, ca: string) => unknown ) => { - changeDurationMultiple( - eventId, - { - hours: value, - minutes: formikValues.scheduleDurationMinutes, - }, - formikValues, - setFieldValue, - checkConflicts - ); - - setFieldValue("scheduleDurationHours", value); + changeDurationMultiple( + eventId, + { + hours: value, + minutes: formikValues.scheduleDurationMinutes, + }, + formikValues, + setFieldValue, + checkConflicts + ); + + setFieldValue("scheduleDurationHours", value); }; export const changeDurationMinuteMultiple = async ( - value: string, - formikValues: RequiredFormikValues & { - scheduleEndDate: string, - scheduleStartDate: string, - scheduleStartHour: string, - scheduleStartMinute: string, - scheduleDurationHours: string, - }, - setFieldValue: (field: string, value: string) => Promise>, - eventId = "", - checkConflicts?: (id: string, startDate: Date, endDate: Date, ca: string) => unknown + value: string, + formikValues: RequiredFormikValues & { + scheduleEndDate: string, + scheduleStartDate: string, + scheduleStartHour: string, + scheduleStartMinute: string, + scheduleDurationHours: string, + }, + setFieldValue: (field: string, value: string) => Promise>, + eventId = "", + checkConflicts?: (id: string, startDate: Date, endDate: Date, ca: string) => unknown ) => { - changeDurationMultiple( - eventId, - { - hours: formikValues.scheduleDurationHours, - minutes: value, - }, - formikValues, - setFieldValue, - checkConflicts - ); - - setFieldValue("scheduleDurationMinutes", value); + changeDurationMultiple( + eventId, + { + hours: formikValues.scheduleDurationHours, + minutes: value, + }, + formikValues, + setFieldValue, + checkConflicts + ); + + setFieldValue("scheduleDurationMinutes", value); }; // get localized time export const localizedMoment = (m: string, currentLanguageCode: string) => { - return moment(m).locale(currentLanguageCode); + return moment(m).locale(currentLanguageCode); }; diff --git a/src/utils/dropDownUtils.ts b/src/utils/dropDownUtils.ts index c3c1b052e7..77d08994ee 100644 --- a/src/utils/dropDownUtils.ts +++ b/src/utils/dropDownUtils.ts @@ -6,36 +6,36 @@ import { isJson } from "./utils"; */ export const filterBySearch = (filterText: string, type: DropDownType, options: any[], t: TFunction) => { - if (type === "language") { - return options.filter((item) => - t(item.name).toLowerCase().includes(filterText) - ); - } else if ( - type === "isPartOf" || - type === "captureAgent" || - type === "aclRole" || - type === "newTheme" - ) { - return options.filter((item) => - item.name.toLowerCase().includes(filterText) - ); - } else if (type === "workflow") { - return options.filter((item) => - item.title.toLowerCase().includes(filterText) - ); - } else if (type === "comment") { - return options.filter((item) => - t(item[0]).toLowerCase().includes(filterText) - ); - } else if (type === "filter") { - return options.filter((item) => - t(item.label).toLowerCase().includes(filterText) - ); - } else { - return options.filter((item) => - item.value.toLowerCase().includes(filterText) - ); - } + if (type === "language") { + return options.filter((item) => + t(item.name).toLowerCase().includes(filterText) + ); + } else if ( + type === "isPartOf" || + type === "captureAgent" || + type === "aclRole" || + type === "newTheme" + ) { + return options.filter((item) => + item.name.toLowerCase().includes(filterText) + ); + } else if (type === "workflow") { + return options.filter((item) => + item.title.toLowerCase().includes(filterText) + ); + } else if (type === "comment") { + return options.filter((item) => + t(item[0]).toLowerCase().includes(filterText) + ); + } else if (type === "filter") { + return options.filter((item) => + t(item.label).toLowerCase().includes(filterText) + ); + } else { + return options.filter((item) => + item.value.toLowerCase().includes(filterText) + ); + } }; /* @@ -45,98 +45,98 @@ export const filterBySearch = (filterText: string, type: DropDownType, options: * as well as adding an empty option, if available */ export const formatDropDownOptions = ( - unformattedOptions: any[], - type: DropDownType, - required: boolean, - t: TFunction + unformattedOptions: any[], + type: DropDownType, + required: boolean, + t: TFunction ) => { - /** - * This is used to determine whether any entry of the passed `unformattedOptions` - * contains an `order` field, indicating that a custom ordering for that list - * exists and the list therefore should not be ordered alphabetically. - */ - const hasCustomOrder = unformattedOptions.every((item: any) => - isJson(item.name) && JSON.parse(item.name).order !== undefined); + /** + * This is used to determine whether any entry of the passed `unformattedOptions` + * contains an `order` field, indicating that a custom ordering for that list + * exists and the list therefore should not be ordered alphabetically. + */ + const hasCustomOrder = unformattedOptions.every((item: any) => + isJson(item.name) && JSON.parse(item.name).order !== undefined); - if (hasCustomOrder) { - // Apply custom ordering. Needs to be done here because the order field isn't carried over - // to the `formattedOptions`. - unformattedOptions.sort((a: any, b: any) => JSON.parse(a.name).order - JSON.parse(b.name).order); - } + if (hasCustomOrder) { + // Apply custom ordering. Needs to be done here because the order field isn't carried over + // to the `formattedOptions`. + unformattedOptions.sort((a: any, b: any) => JSON.parse(a.name).order - JSON.parse(b.name).order); + } - const formattedOptions = []; - if (!required) { - formattedOptions.push({ - value: "", - label: `-- ${t("SELECT_NO_OPTION_SELECTED")} --`, - }); - } - if (type === "language" || type === "license") { - for (const item of unformattedOptions) { - formattedOptions.push({ - value: item.value, - label: t(item.name), - }); - } - } else if (type === "isPartOf") { - for (const item of unformattedOptions) { - formattedOptions.push({ - value: item.value, - label: item.name, - }); - } - } else if (type === "captureAgent" || type === "aclRole") { - for (const item of unformattedOptions) { - formattedOptions.push({ - value: item.name, - label: item.name, - }); - } - } else if (type === "workflow") { - for (const item of unformattedOptions) { - formattedOptions.push({ - value: item.id, - label: item.title, - }); - } - } else if (type === "aclTemplate") { - for (const item of unformattedOptions) { - formattedOptions.push({ - value: item.id, - label: item.value, - }); - } - } else if (type === "newTheme") { - for (const item of unformattedOptions) { - formattedOptions.push({ - value: item.id, - label: item.name, - }); - } - } else if (type === "comment") { - for (const item of unformattedOptions) { - formattedOptions.push({ - value: item[0], - label: t(item[1]), - }); - } - } else if (type === "filter") { - for (const item of unformattedOptions) { - formattedOptions.push({ - value: item.value, - label: item.label, - }); - } - } else { - for (const item of unformattedOptions) { - formattedOptions.push({ - value: item.value, - label: item.value, - }); - } - } + const formattedOptions = []; + if (!required) { + formattedOptions.push({ + value: "", + label: `-- ${t("SELECT_NO_OPTION_SELECTED")} --`, + }); + } + if (type === "language" || type === "license") { + for (const item of unformattedOptions) { + formattedOptions.push({ + value: item.value, + label: t(item.name), + }); + } + } else if (type === "isPartOf") { + for (const item of unformattedOptions) { + formattedOptions.push({ + value: item.value, + label: item.name, + }); + } + } else if (type === "captureAgent" || type === "aclRole") { + for (const item of unformattedOptions) { + formattedOptions.push({ + value: item.name, + label: item.name, + }); + } + } else if (type === "workflow") { + for (const item of unformattedOptions) { + formattedOptions.push({ + value: item.id, + label: item.title, + }); + } + } else if (type === "aclTemplate") { + for (const item of unformattedOptions) { + formattedOptions.push({ + value: item.id, + label: item.value, + }); + } + } else if (type === "newTheme") { + for (const item of unformattedOptions) { + formattedOptions.push({ + value: item.id, + label: item.name, + }); + } + } else if (type === "comment") { + for (const item of unformattedOptions) { + formattedOptions.push({ + value: item[0], + label: t(item[1]), + }); + } + } else if (type === "filter") { + for (const item of unformattedOptions) { + formattedOptions.push({ + value: item.value, + label: item.label, + }); + } + } else { + for (const item of unformattedOptions) { + formattedOptions.push({ + value: item.value, + label: item.value, + }); + } + } - return hasCustomOrder - ? formattedOptions - : formattedOptions.sort((a, b) => a.label.localeCompare(b.label)); + return hasCustomOrder + ? formattedOptions + : formattedOptions.sort((a, b) => a.label.localeCompare(b.label)); }; diff --git a/src/utils/embeddedCodeUtils.ts b/src/utils/embeddedCodeUtils.ts index 7a61798136..952975e6b9 100644 --- a/src/utils/embeddedCodeUtils.ts +++ b/src/utils/embeddedCodeUtils.ts @@ -1,20 +1,20 @@ import axios from "axios"; export const getSourceURL = async () => { - try { - // get source url - const response = await axios.get( - "/api/info/organization/properties/engageuiurl" - ); + try { + // get source url + const response = await axios.get( + "/api/info/organization/properties/engageuiurl" + ); - let data = await response.data; + let data = await response.data; - if (data["org.opencastproject.engage.ui.url"]) { - return data["org.opencastproject.engage.ui.url"]; - } else { - return ""; - } - } catch (e) { - return ""; - } + if (data["org.opencastproject.engage.ui.url"]) { + return data["org.opencastproject.engage.ui.url"]; + } else { + return ""; + } + } catch (e) { + return ""; + } }; diff --git a/src/utils/eventDetailsUtils.ts b/src/utils/eventDetailsUtils.ts index f100e335ec..7bb88ee7a8 100644 --- a/src/utils/eventDetailsUtils.ts +++ b/src/utils/eventDetailsUtils.ts @@ -6,65 +6,65 @@ import { Event } from "../slices/eventSlice"; */ export const style_nav = { - borderBottom: "1px solid #d6d6d6", - lineHeight: "35px", + borderBottom: "1px solid #d6d6d6", + lineHeight: "35px", }; export const style_nav_hierarchy_inactive = { - marginLeft: "30px", - color: "#92a0ab", + marginLeft: "30px", + color: "#92a0ab", }; export const style_nav_hierarchy = { - marginLeft: "30px", - marginRight: "30px", - fontWeight: "600", - color: "#5d7589", + marginLeft: "30px", + marginRight: "30px", + fontWeight: "600", + color: "#5d7589", }; export const style_button_spacing = { - marginTop: "13px", - marginLeft: "15px", - marginRight: "15px", + marginTop: "13px", + marginLeft: "15px", + marginRight: "15px", }; export const error_detail_style = { - overflow: "auto", - width: "750px", + overflow: "auto", + width: "750px", }; export const formatDuration = (durationInMS: number) => { - const duration = moment.duration(durationInMS); - if (duration.asHours() > 1) { - return moment.utc(duration.asMilliseconds()).format("HH:mm:ss"); - } else { - return moment.utc(duration.asMilliseconds()).format("mm:ss"); - } + const duration = moment.duration(durationInMS); + if (duration.asHours() > 1) { + return moment.utc(duration.asMilliseconds()).format("HH:mm:ss"); + } else { + return moment.utc(duration.asMilliseconds()).format("mm:ss"); + } }; export const humanReadableBytesFilter = (bytesValue: string | number) => { - // best effort, independent on type - let bytes = bytesValue; - if (typeof bytes === "string") { - return bytesValue; - } + // best effort, independent on type + let bytes = bytesValue; + if (typeof bytes === "string") { + return bytesValue; + } - // from http://stackoverflow.com/a/14919494 - const thresh = 1000; - if (Math.abs(bytes) < thresh) { - return bytes + " B"; - } - const units = ["kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"]; - let u = -1; - do { - bytes /= thresh; - ++u; - } while (Math.abs(bytes) >= thresh && u < units.length - 1); + // from http://stackoverflow.com/a/14919494 + const thresh = 1000; + if (Math.abs(bytes) < thresh) { + return bytes + " B"; + } + const units = ["kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"]; + let u = -1; + do { + bytes /= thresh; + ++u; + } while (Math.abs(bytes) >= thresh && u < units.length - 1); - return bytes.toFixed(1) + " " + units[u]; + return bytes.toFixed(1) + " " + units[u]; }; export const hasScheduledStatus = (event: Event) => { - return event.event_status.toUpperCase().indexOf("SCHEDULED") > -1 || - event.event_status.toUpperCase().indexOf("RECORDING") > -1 + return event.event_status.toUpperCase().indexOf("SCHEDULED") > -1 || + event.event_status.toUpperCase().indexOf("RECORDING") > -1 }; diff --git a/src/utils/resourceUtils.ts b/src/utils/resourceUtils.ts index 6f523cd172..5d9fdbbb24 100644 --- a/src/utils/resourceUtils.ts +++ b/src/utils/resourceUtils.ts @@ -1,9 +1,9 @@ import { getFilters, getTextFilter } from "../selectors/tableFilterSelectors"; import { - getPageLimit, - getPageOffset, - getTableDirection, - getTableSorting, + getPageLimit, + getPageOffset, + getTableDirection, + getTableSorting, } from "../selectors/tableSelectors"; import { TransformedAcl } from "../slices/aclDetailsSlice"; import { Acl } from "../slices/aclSlice"; @@ -24,444 +24,444 @@ import { TableState } from "../slices/tableSlice"; // prepare http headers for posting to resources export const getHttpHeaders = () => { - return { - headers: { - "Content-Type": "application/x-www-form-urlencoded", - }, - }; + return { + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + }; }; // prepare URL params for getting resources export const getURLParams = ( - state: RootState, - resource: TableState["resource"], + state: RootState, + resource: TableState["resource"], ) => { - // get filter map from state - let filters = []; - let filterMap = getFilters(state, resource); - let textFilter = getTextFilter(state); - - // check if textFilter has value and transform for use as URL param - if (textFilter !== "") { - filters.push(["textFilter", textFilter]); - } - // transform filters for use as URL param - for (let key in filterMap) { - if (!!filterMap[key].value) { - filters.push([filterMap[key].name, filterMap[key].value.toString()]); - } - } - - let params: { - limit: number, - offset: number, - filter?: string, - sort?: string, - } = { - limit: getPageLimit(state), - offset: getPageOffset(state) * getPageLimit(state), - }; - - if (filters.length) { - params = { - ...params, - filter: filters - .map(([key, value]) => `${key}:${encodeURIComponent(value)}`) - .join(","), - }; - } - - if (!!getTableSorting(state)) { - params = { - ...params, - sort: getTableSorting(state) + ":" + getTableDirection(state), - }; - } - - return params; + // get filter map from state + let filters = []; + let filterMap = getFilters(state, resource); + let textFilter = getTextFilter(state); + + // check if textFilter has value and transform for use as URL param + if (textFilter !== "") { + filters.push(["textFilter", textFilter]); + } + // transform filters for use as URL param + for (let key in filterMap) { + if (!!filterMap[key].value) { + filters.push([filterMap[key].name, filterMap[key].value.toString()]); + } + } + + let params: { + limit: number, + offset: number, + filter?: string, + sort?: string, + } = { + limit: getPageLimit(state), + offset: getPageOffset(state) * getPageLimit(state), + }; + + if (filters.length) { + params = { + ...params, + filter: filters + .map(([key, value]) => `${key}:${encodeURIComponent(value)}`) + .join(","), + }; + } + + if (!!getTableSorting(state)) { + params = { + ...params, + sort: getTableSorting(state) + ":" + getTableDirection(state), + }; + } + + return params; }; // used for create URLSearchParams for API requests used to create/update user export const buildUserBody = (values: NewUser | UpdateUser) => { - let data = new URLSearchParams(); - // fill form data with user inputs - data.append("username", values.username); - values.name && data.append("name", values.name); - values.email && data.append("email", values.email); - values.password && data.append("password", values.password); - values.roles && data.append("roles", JSON.stringify(values.roles)); - - return data; + let data = new URLSearchParams(); + // fill form data with user inputs + data.append("username", values.username); + values.name && data.append("name", values.name); + values.email && data.append("email", values.email); + values.password && data.append("password", values.password); + values.roles && data.append("roles", JSON.stringify(values.roles)); + + return data; }; // used for create URLSearchParams for API requests used to create/update group export const buildGroupBody = ( - values: typeof initialFormValuesNewGroup + values: typeof initialFormValuesNewGroup ) => { - let roles = [], - users = []; - - // fill form data depending on user inputs - let data = new URLSearchParams(); - data.append("name", values.name); - data.append("description", values.description); - - for (let i = 0; i < values.roles.length; i++) { - roles.push(values.roles[i].name); - } - for (let i = 0; i < values.users.length; i++) { - users.push(values.users[i].id); - } - data.append("roles", roles.join(",")); - data.append("users", users.join(",")); - - return data; + let roles = [], + users = []; + + // fill form data depending on user inputs + let data = new URLSearchParams(); + data.append("name", values.name); + data.append("description", values.description); + + for (let i = 0; i < values.roles.length; i++) { + roles.push(values.roles[i].name); + } + for (let i = 0; i < values.users.length; i++) { + users.push(values.users[i].id); + } + data.append("roles", roles.join(",")); + data.append("users", users.join(",")); + + return data; }; // get initial metadata field values for formik in create resources wizards export const getInitialMetadataFieldValues = ( - metadataCatalog: MetadataCatalog, + metadataCatalog: MetadataCatalog, ) => { - let initialValues: { [key: string]: string | string[] | boolean } = {}; + let initialValues: { [key: string]: string | string[] | boolean } = {}; - if (!!metadataCatalog.fields && metadataCatalog.fields.length > 0) { - metadataCatalog.fields.forEach((field) => { - initialValues[metadataCatalog.flavor + "_" + field.id] = field.value; - }); - } + if (!!metadataCatalog.fields && metadataCatalog.fields.length > 0) { + metadataCatalog.fields.forEach((field) => { + initialValues[metadataCatalog.flavor + "_" + field.id] = field.value; + }); + } - return initialValues; + return initialValues; }; // transform collection of metadata into object with name and value export const transformMetadataCollection = (metadata: MetadataCatalog) => { - transformMetadataFields(metadata.fields); - return metadata; + transformMetadataFields(metadata.fields); + return metadata; }; export const transformMetadataFields = (metadata: MetadataField[]) => { - for (const field of metadata) { - if (field.collection) { - field.collection = Object.entries(field.collection) - .map(([key, value]) => { - if (isJson(key)) { - let collectionParsed = JSON.parse(key); - return { - name: collectionParsed.label || key, - value, - ...collectionParsed, - }; - } else { - return { - name: key, - value: value, - }; - } - }); - } - } - return metadata; + for (const field of metadata) { + if (field.collection) { + field.collection = Object.entries(field.collection) + .map(([key, value]) => { + if (isJson(key)) { + let collectionParsed = JSON.parse(key); + return { + name: collectionParsed.label || key, + value, + ...collectionParsed, + }; + } else { + return { + name: key, + value: value, + }; + } + }); + } + } + return metadata; }; // transform metadata catalog for update via post request export const transformMetadataForUpdate = (catalog: MetadataCatalog, values: { [key: string]: MetadataCatalog["fields"][0]["value"] }) => { - let fields: MetadataCatalog["fields"] = []; - let updatedFields: MetadataCatalog["fields"] = []; - - catalog.fields.forEach((field) => { - if (field.value !== values[field.id]) { - let updatedField = { - ...field, - value: values[field.id], - }; - updatedFields.push(updatedField); - fields.push(updatedField); - } else { - fields.push({ ...field }); - } - }); - let data = new URLSearchParams(); - data.append( - "metadata", - JSON.stringify([ - { - flavor: catalog.flavor, - title: catalog.title, - fields: updatedFields, - }, - ]) - ); - const headers = getHttpHeaders(); - - return { fields, data, headers }; + let fields: MetadataCatalog["fields"] = []; + let updatedFields: MetadataCatalog["fields"] = []; + + catalog.fields.forEach((field) => { + if (field.value !== values[field.id]) { + let updatedField = { + ...field, + value: values[field.id], + }; + updatedFields.push(updatedField); + fields.push(updatedField); + } else { + fields.push({ ...field }); + } + }); + let data = new URLSearchParams(); + data.append( + "metadata", + JSON.stringify([ + { + flavor: catalog.flavor, + title: catalog.title, + fields: updatedFields, + }, + ]) + ); + const headers = getHttpHeaders(); + + return { fields, data, headers }; }; // Prepare metadata for post of new events or series export const prepareMetadataFieldsForPost = ( - metadataCatalogs: MetadataCatalog[], - values: { [key: string]: unknown }, + metadataCatalogs: MetadataCatalog[], + values: { [key: string]: unknown }, ) => { - const preparedMetadataCatalogs = []; - - for (const catalog of metadataCatalogs) { - const catalogPrefix = catalog.flavor + "_"; - - type FieldValue = { - id: string, - type: string, - value: unknown, - $$hashKey?: string, - translatable?: boolean, - } - let metadataFields: FieldValue[] = []; - - // fill metadataField with field information send by server previously and values provided by user - for (const [, info] of catalog.fields.entries()) { - let fieldValue: FieldValue = { - id: info.id, - type: info.type, - value: values[catalogPrefix + info.id], - $$hashKey: "object:123", - }; - if (!!info.translatable) { - fieldValue = { - ...fieldValue, - translatable: info.translatable, - }; - } - metadataFields = metadataFields.concat(fieldValue); - } - - const metadataCatalog = { - flavor: catalog.flavor, - title: catalog.title, - fields: metadataFields, - }; - - preparedMetadataCatalogs.push(metadataCatalog); - } - - return preparedMetadataCatalogs; + const preparedMetadataCatalogs = []; + + for (const catalog of metadataCatalogs) { + const catalogPrefix = catalog.flavor + "_"; + + type FieldValue = { + id: string, + type: string, + value: unknown, + $$hashKey?: string, + translatable?: boolean, + } + let metadataFields: FieldValue[] = []; + + // fill metadataField with field information send by server previously and values provided by user + for (const [, info] of catalog.fields.entries()) { + let fieldValue: FieldValue = { + id: info.id, + type: info.type, + value: values[catalogPrefix + info.id], + $$hashKey: "object:123", + }; + if (!!info.translatable) { + fieldValue = { + ...fieldValue, + translatable: info.translatable, + }; + } + metadataFields = metadataFields.concat(fieldValue); + } + + const metadataCatalog = { + flavor: catalog.flavor, + title: catalog.title, + fields: metadataFields, + }; + + preparedMetadataCatalogs.push(metadataCatalog); + } + + return preparedMetadataCatalogs; }; // returns the name for a field value from the collection export const getMetadataCollectionFieldName = (metadataField: { collection?: { [key: string]: unknown }[] }, field: { value: unknown }, t: TFunction) => { - try { - if (!!metadataField.collection) { - const collectionField = metadataField.collection.find( - (element) => element.value === field.value - ); - - if (collectionField && isJson(collectionField.name as string)) { - return t(JSON.parse(collectionField.name as string).label); - } - - return collectionField ? t(collectionField.name as string) : ""; - } - - return ""; - } catch (e) { - return ""; - } + try { + if (!!metadataField.collection) { + const collectionField = metadataField.collection.find( + (element) => element.value === field.value + ); + + if (collectionField && isJson(collectionField.name as string)) { + return t(JSON.parse(collectionField.name as string).label); + } + + return collectionField ? t(collectionField.name as string) : ""; + } + + return ""; + } catch (e) { + return ""; + } }; // Prepare rules of access policies for post of new events or series export const prepareAccessPolicyRulesForPost = (policies: TransformedAcl[]) => { - // access policies for post request - let access : { - acl : Acl - } = { - acl: { - ace: [], - }, - }; - - // iterate through all policies provided by user and transform them into form required for request - for (let i = 0; policies.length > i; i++) { - if (policies[i].read) { - access.acl.ace = access.acl.ace.concat({ - action: "read", - allow: policies[i].read, - role: policies[i].role, - }); - } - if (policies[i].write) { - access.acl.ace = access.acl.ace.concat({ - action: "write", - allow: policies[i].write, - role: policies[i].role, - }); - } - if (policies[i].actions.length > 0) { - for (let j = 0; policies[i].actions.length > j; j++) { - access.acl.ace = access.acl.ace.concat({ - action: policies[i].actions[j], - allow: true, - role: policies[i].role, - }); - } - } - } - - return access; + // access policies for post request + let access : { + acl : Acl + } = { + acl: { + ace: [], + }, + }; + + // iterate through all policies provided by user and transform them into form required for request + for (let i = 0; policies.length > i; i++) { + if (policies[i].read) { + access.acl.ace = access.acl.ace.concat({ + action: "read", + allow: policies[i].read, + role: policies[i].role, + }); + } + if (policies[i].write) { + access.acl.ace = access.acl.ace.concat({ + action: "write", + allow: policies[i].write, + role: policies[i].role, + }); + } + if (policies[i].actions.length > 0) { + for (let j = 0; policies[i].actions.length > j; j++) { + access.acl.ace = access.acl.ace.concat({ + action: policies[i].actions[j], + allow: true, + role: policies[i].role, + }); + } + } + } + + return access; }; // transform response data in form that is used in wizards and modals for policies (for each role one entry) export const transformAclTemplatesResponse = (acl: Acl) => { - let template: TransformedAcl[] = []; - - for (let i = 0; acl.ace.length > i; i++) { - if (template.find((rule) => rule.role === acl.ace[i].role)) { - for (let j = 0; template.length > j; j++) { - // Only update entry for policy if already added with other action - if (template[j].role === acl.ace[i].role) { - if (acl.ace[i].action === "read") { - template[j] = { - ...template[j], - read: acl.ace[i].allow, - }; - break; - } - if (acl.ace[i].action === "write") { - template[j] = { - ...template[j], - write: acl.ace[i].allow, - }; - break; - } - if ( - acl.ace[i].action !== "read" && - acl.ace[i].action !== "write" && - acl.ace[i].allow === true - ) { - template[j] = { - ...template[j], - actions: template[j].actions.concat(acl.ace[i].action), - }; - break; - } - } - } - } else { - // add policy if role not seen before - if (acl.ace[i].action === "read") { - template = template.concat({ - role: acl.ace[i].role, - read: acl.ace[i].allow, - write: false, - actions: [], - }); - } - if (acl.ace[i].action === "write") { - template = template.concat({ - role: acl.ace[i].role, - read: false, - write: acl.ace[i].allow, - actions: [], - }); - } - if ( - acl.ace[i].action !== "read" && - acl.ace[i].action !== "write" && - acl.ace[i].allow === true - ) { - template = template.concat({ - role: acl.ace[i].role, - read: false, - write: false, - actions: [acl.ace[i].action], - }); - } - } - } - - return template; + let template: TransformedAcl[] = []; + + for (let i = 0; acl.ace.length > i; i++) { + if (template.find((rule) => rule.role === acl.ace[i].role)) { + for (let j = 0; template.length > j; j++) { + // Only update entry for policy if already added with other action + if (template[j].role === acl.ace[i].role) { + if (acl.ace[i].action === "read") { + template[j] = { + ...template[j], + read: acl.ace[i].allow, + }; + break; + } + if (acl.ace[i].action === "write") { + template[j] = { + ...template[j], + write: acl.ace[i].allow, + }; + break; + } + if ( + acl.ace[i].action !== "read" && + acl.ace[i].action !== "write" && + acl.ace[i].allow === true + ) { + template[j] = { + ...template[j], + actions: template[j].actions.concat(acl.ace[i].action), + }; + break; + } + } + } + } else { + // add policy if role not seen before + if (acl.ace[i].action === "read") { + template = template.concat({ + role: acl.ace[i].role, + read: acl.ace[i].allow, + write: false, + actions: [], + }); + } + if (acl.ace[i].action === "write") { + template = template.concat({ + role: acl.ace[i].role, + read: false, + write: acl.ace[i].allow, + actions: [], + }); + } + if ( + acl.ace[i].action !== "read" && + acl.ace[i].action !== "write" && + acl.ace[i].allow === true + ) { + template = template.concat({ + role: acl.ace[i].role, + read: false, + write: false, + actions: [acl.ace[i].action], + }); + } + } + } + + return template; }; // filter devices, so that only devices for which the user has access rights are left export const filterDevicesForAccess = (user: UserInfoState, inputDevices: Recording[]) => { - if (user.isOrgAdmin) { - return inputDevices; - } else { - const devicesWithAccessRights = []; - for (const device of inputDevices) { - const inputDeviceAccessRole = - "ROLE_CAPTURE_AGENT_" + - device.id.replace(/[^a-zA-Z0-9_]/g, "").toUpperCase(); - if (hasAccess(inputDeviceAccessRole, user)) { - devicesWithAccessRights.push(device); - } - } - - return devicesWithAccessRights; - } + if (user.isOrgAdmin) { + return inputDevices; + } else { + const devicesWithAccessRights = []; + for (const device of inputDevices) { + const inputDeviceAccessRole = + "ROLE_CAPTURE_AGENT_" + + device.id.replace(/[^a-zA-Z0-9_]/g, "").toUpperCase(); + if (hasAccess(inputDeviceAccessRole, user)) { + devicesWithAccessRights.push(device); + } + } + + return devicesWithAccessRights; + } }; // returns, whether user has access rights for any inputDevices export const hasAnyDeviceAccess = (user: UserInfoState, inputDevices: Recording[]) => { - return filterDevicesForAccess(user, inputDevices).length > 0; + return filterDevicesForAccess(user, inputDevices).length > 0; }; // returns, whether user has access rights for a specific inputDevice export const hasDeviceAccess = (user: UserInfoState, deviceId: Recording["id"]) => { - if (user.isOrgAdmin) { - return true; - } else { - const inputDeviceAccessRole = - "ROLE_CAPTURE_AGENT_" + - deviceId.replace(/[^a-zA-Z0-9_]/g, "").toUpperCase(); - return hasAccess(inputDeviceAccessRole, user); - } + if (user.isOrgAdmin) { + return true; + } else { + const inputDeviceAccessRole = + "ROLE_CAPTURE_AGENT_" + + deviceId.replace(/[^a-zA-Z0-9_]/g, "").toUpperCase(); + return hasAccess(inputDeviceAccessRole, user); + } }; // build body for post/put request in theme context export const buildThemeBody = (values: { - name: string, - description: string, - bumperActive: boolean, - bumperFile: string, - trailerActive: boolean, - trailerFile: string, - titleSlideActive: boolean, - titleSlideMode: string, - titleSlideBackground: string, - licenseSlideActive: boolean, - watermarkActive: boolean, - watermarkFile: string, - watermarkPosition: string, + name: string, + description: string, + bumperActive: boolean, + bumperFile: string, + trailerActive: boolean, + trailerFile: string, + titleSlideActive: boolean, + titleSlideMode: string, + titleSlideBackground: string, + licenseSlideActive: boolean, + watermarkActive: boolean, + watermarkFile: string, + watermarkPosition: string, }) => { - // fill form data depending on user inputs - let data = new URLSearchParams(); - data.append("name", values.name); - data.append("description", values.description); - data.append("bumperActive", values.bumperActive.toString()); - if (values.bumperActive) { - data.append("bumperFile", values.bumperFile); - } - data.append("trailerActive", values.trailerActive.toString()); - if (values.trailerActive) { - data.append("trailerFile", values.trailerFile); - } - data.append("titleSlideActive", values.titleSlideActive.toString()); - if (values.titleSlideActive && values.titleSlideMode === "upload") { - data.append("titleSlideBackground", values.titleSlideBackground); - } - data.append("licenseSlideActive", values.licenseSlideActive.toString()); - data.append("watermarkActive", values.watermarkActive.toString()); - if (values.watermarkActive) { - data.append("watermarkFile", values.watermarkFile); - data.append("watermarkPosition", values.watermarkPosition); - } - - return data; + // fill form data depending on user inputs + let data = new URLSearchParams(); + data.append("name", values.name); + data.append("description", values.description); + data.append("bumperActive", values.bumperActive.toString()); + if (values.bumperActive) { + data.append("bumperFile", values.bumperFile); + } + data.append("trailerActive", values.trailerActive.toString()); + if (values.trailerActive) { + data.append("trailerFile", values.trailerFile); + } + data.append("titleSlideActive", values.titleSlideActive.toString()); + if (values.titleSlideActive && values.titleSlideMode === "upload") { + data.append("titleSlideBackground", values.titleSlideBackground); + } + data.append("licenseSlideActive", values.licenseSlideActive.toString()); + data.append("watermarkActive", values.watermarkActive.toString()); + if (values.watermarkActive) { + data.append("watermarkFile", values.watermarkFile); + data.append("watermarkPosition", values.watermarkPosition); + } + + return data; }; // creates an empty policy with the role from the argument export const createPolicy = (role: string): TransformedAcl => { - return { - role: role, - read: false, - write: false, - actions: [], - }; + return { + role: role, + read: false, + write: false, + actions: [], + }; }; diff --git a/src/utils/statisticsUtils.ts b/src/utils/statisticsUtils.ts index 001c553c4a..ee53ac1bb1 100644 --- a/src/utils/statisticsUtils.ts +++ b/src/utils/statisticsUtils.ts @@ -10,148 +10,148 @@ import type { ChartOptions, TooltipItem } from 'chart.js'; /* creates callback function for formatting the labels of the xAxis in a statistics diagram */ function createXAxisTickCallback ( - timeMode: TimeMode, - dataResolution: DataResolution, - language: string, + timeMode: TimeMode, + dataResolution: DataResolution, + language: string, ) { - let formatString = "L"; - if (timeMode === "year") { - formatString = "MMMM"; - } else if (timeMode === "month") { - formatString = "dddd, Do"; - } else { - if (dataResolution === "yearly") { - formatString = "YYYY"; - } else if (dataResolution === "monthly") { - formatString = "MMMM"; - } else if (dataResolution === "daily") { - if (language === "en-US" || language === "en-GB") { - formatString = "MMMM Do, YYYY"; - } else { - formatString = "Do MMMM YYYY"; - } - } else if (dataResolution === "hourly") { - formatString = "LLL"; - } - } + let formatString = "L"; + if (timeMode === "year") { + formatString = "MMMM"; + } else if (timeMode === "month") { + formatString = "dddd, Do"; + } else { + if (dataResolution === "yearly") { + formatString = "YYYY"; + } else if (dataResolution === "monthly") { + formatString = "MMMM"; + } else if (dataResolution === "daily") { + if (language === "en-US" || language === "en-GB") { + formatString = "MMMM Do, YYYY"; + } else { + formatString = "Do MMMM YYYY"; + } + } else if (dataResolution === "hourly") { + formatString = "LLL"; + } + } - return function (tickValue: number | string) { - // Typescript does not like "this", but the chart.js documentation insists we should do it this way - // @ts-ignore - return moment(this.getLabelForValue(tickValue)).locale(language).format(formatString); - }; + return function (tickValue: number | string) { + // Typescript does not like "this", but the chart.js documentation insists we should do it this way + // @ts-ignore + return moment(this.getLabelForValue(tickValue)).locale(language).format(formatString); + }; }; /* creates callback function for the displayed label when hovering over a data point in a statistics diagram */ const createTooltipCallback = ( - timeMode: TimeMode, - dataResolution: DataResolution, - language: string + timeMode: TimeMode, + dataResolution: DataResolution, + language: string ) => { - let formatString; - if (timeMode === "year") { - formatString = "MMMM YYYY"; - } else if (timeMode === "month") { - if (language === "en-US" || language === "en-GB") { - formatString = "dddd, MMMM Do, YYYY"; - } else { - formatString = "dddd, Do MMMM YYYY"; - } - } else { - if (dataResolution === "yearly") { - formatString = "YYYY"; - } else if (dataResolution === "monthly") { - formatString = "MMMM YYYY"; - } else if (dataResolution === "daily") { - if (language === "en-US" || language === "en-GB") { - formatString = "dddd, MMMM Do, YYYY"; - } else { - formatString = "dddd, Do MMMM YYYY"; - } - } else { - if (language === "en-US" || language === "en-GB") { - formatString = "dddd, MMMM Do, YYYY HH:mm"; - } else { - formatString = "dddd, Do MMMM YYYY, HH:mm"; - } - } - } + let formatString; + if (timeMode === "year") { + formatString = "MMMM YYYY"; + } else if (timeMode === "month") { + if (language === "en-US" || language === "en-GB") { + formatString = "dddd, MMMM Do, YYYY"; + } else { + formatString = "dddd, Do MMMM YYYY"; + } + } else { + if (dataResolution === "yearly") { + formatString = "YYYY"; + } else if (dataResolution === "monthly") { + formatString = "MMMM YYYY"; + } else if (dataResolution === "daily") { + if (language === "en-US" || language === "en-GB") { + formatString = "dddd, MMMM Do, YYYY"; + } else { + formatString = "dddd, Do MMMM YYYY"; + } + } else { + if (language === "en-US" || language === "en-GB") { + formatString = "dddd, MMMM Do, YYYY HH:mm"; + } else { + formatString = "dddd, Do MMMM YYYY, HH:mm"; + } + } + } - return (tooltipItem: TooltipItem<"bar">) => { - const date = tooltipItem.label; - const finalDate = moment(date).locale(language).format(formatString); - return finalDate + ": " + tooltipItem.formattedValue; - }; + return (tooltipItem: TooltipItem<"bar">) => { + const date = tooltipItem.label; + const finalDate = moment(date).locale(language).format(formatString); + return finalDate + ": " + tooltipItem.formattedValue; + }; }; /* creates options for statistics chart */ export const createChartOptions = ( - timeMode: TimeMode, - dataResolution: DataResolution + timeMode: TimeMode, + dataResolution: DataResolution ): ChartOptions<'bar'> => { - // Get info about the current language and its date locale - const currentLanguageInfo = getCurrentLanguageInformation(); - let currentLanguage = ""; - if (currentLanguageInfo) { - currentLanguage = currentLanguageInfo.dateLocale.code - } + // Get info about the current language and its date locale + const currentLanguageInfo = getCurrentLanguageInformation(); + let currentLanguage = ""; + if (currentLanguageInfo) { + currentLanguage = currentLanguageInfo.dateLocale.code + } - return { - responsive: true, - plugins: { - legend: { - display: false, - }, - tooltip: { - callbacks: { - label: createTooltipCallback(timeMode, dataResolution, currentLanguage), - }, - }, - }, - layout: { - padding: { - top: 20, - left: 20, - right: 20, - }, - }, - scales: { - x: - { - ticks: { - callback: createXAxisTickCallback( - timeMode, - dataResolution, - currentLanguage, - ), - }, - }, + return { + responsive: true, + plugins: { + legend: { + display: false, + }, + tooltip: { + callbacks: { + label: createTooltipCallback(timeMode, dataResolution, currentLanguage), + }, + }, + }, + layout: { + padding: { + top: 20, + left: 20, + right: 20, + }, + }, + scales: { + x: + { + ticks: { + callback: createXAxisTickCallback( + timeMode, + dataResolution, + currentLanguage, + ), + }, + }, - y: { - suggestedMin: 0, - }, - }, + y: { + suggestedMin: 0, + }, + }, - }; + }; }; /* creates the url for downloading a csv file with current statistics */ export const createDownloadUrl = ( - resourceId: string, - resourceType: string, - providerId: string, - from: Date | string, - to: Date | string, - dataResolution: string + resourceId: string, + resourceType: string, + providerId: string, + from: Date | string, + to: Date | string, + dataResolution: string ) => { - const csvUrlSearchParams = new URLSearchParams({ - dataResolution: dataResolution, - providerId: providerId, - resourceId: resourceId, - resourceType: resourceType, - from: moment(from).toJSON(), - to: moment(to).endOf("day").toJSON(), - }); + const csvUrlSearchParams = new URLSearchParams({ + dataResolution: dataResolution, + providerId: providerId, + resourceId: resourceId, + resourceType: resourceType, + from: moment(from).toJSON(), + to: moment(to).endOf("day").toJSON(), + }); - return "/admin-ng/statistics/export.csv?" + csvUrlSearchParams; + return "/admin-ng/statistics/export.csv?" + csvUrlSearchParams; }; diff --git a/src/utils/utils.ts b/src/utils/utils.ts index f2c67006b7..a9f8688c1d 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -9,50 +9,50 @@ import { UploadAssetOption } from "../slices/eventSlice"; */ export const getTimezoneOffset = () => { - let d = new Date(); - let offset = d.getTimezoneOffset() * -1; + let d = new Date(); + let offset = d.getTimezoneOffset() * -1; - return offset / 60; + return offset / 60; }; export const getTimezoneString = (offset: number) => { - return "UTC" + (offset < 0 ? "-" : "+") + offset; + return "UTC" + (offset < 0 ? "-" : "+") + offset; }; export const getCurrentLanguageInformation = () => { - // Get code, flag, name and date locale of the current language - let currentLang = languages.find(({ code }) => code === i18n.language); - if (typeof currentLang === "undefined") { - // If detected language code, like "de-CH", isn't part of translations try 2-digit language code - currentLang = languages.find(({ code }) => code === i18n.language.split("-")[0]); - if (typeof currentLang === "undefined") { - currentLang = languages.find(({ code }) => code === "en-US"); - } - } - - return currentLang; + // Get code, flag, name and date locale of the current language + let currentLang = languages.find(({ code }) => code === i18n.language); + if (typeof currentLang === "undefined") { + // If detected language code, like "de-CH", isn't part of translations try 2-digit language code + currentLang = languages.find(({ code }) => code === i18n.language.split("-")[0]); + if (typeof currentLang === "undefined") { + currentLang = languages.find(({ code }) => code === "en-US"); + } + } + + return currentLang; }; // fills an array from 00 to number of elements specified export const initArray = (numberOfElements: number) => { - let i, - result = []; - for (i = 0; i < numberOfElements; i++) { - result.push({ - index: i, - value: makeTwoDigits(i), - }); - } - return result; + let i, + result = []; + for (i = 0; i < numberOfElements; i++) { + result.push({ + index: i, + value: makeTwoDigits(i), + }); + } + return result; }; // insert leading 0 for numbers smaller 10 export const makeTwoDigits = (number: number) => { - if (number < 10) { - return "0" + number; - } else { - return "" + number; - } + if (number < 10) { + return "0" + number; + } else { + return "" + number; + } }; /* @@ -60,12 +60,12 @@ export const makeTwoDigits = (number: number) => { * to [{id: id1, value: value1},{id: id2, value: value2}] */ export const transformToIdValueArray = (data: {[key: string | number]: string}) => { - return Object.keys(data).map((key) => { - return { - id: key, - value: data[key], - }; - }); + return Object.keys(data).map((key) => { + return { + id: key, + value: data[key], + }; + }); }; /* @@ -73,13 +73,13 @@ export const transformToIdValueArray = (data: {[key: string | number]: string}) * to their corresponding boolean value. All other values stay the same. */ export const parseBooleanInObject = (baseObject: {[key: string]: unknown}) => { - let parsedObject: {[key: string]: unknown} = {}; + let parsedObject: {[key: string]: unknown} = {}; - Object.keys(baseObject).forEach((config) => { - parsedObject[config] = parseValueForBooleanStrings(baseObject[config]); - }); + Object.keys(baseObject).forEach((config) => { + parsedObject[config] = parseValueForBooleanStrings(baseObject[config]); + }); - return parsedObject; + return parsedObject; }; /* @@ -87,32 +87,32 @@ export const parseBooleanInObject = (baseObject: {[key: string]: unknown}) => { * to their corresponding boolean value. All other kinds of values stay the same. */ export const parseValueForBooleanStrings = (value: unknown) => { - let parsedValue = value; - if (parsedValue === "true") { - parsedValue = true; - } else if (parsedValue === "false") { - parsedValue = false; - } - - return parsedValue; + let parsedValue = value; + if (parsedValue === "true") { + parsedValue = true; + } else if (parsedValue === "false") { + parsedValue = false; + } + + return parsedValue; }; /* * checks if a user is admin or has the required role to access an ui element */ export const hasAccess = (role: string, userInfo: UserInfoState) => { - return !!(userInfo.isAdmin || userInfo.roles.includes(role)); + return !!(userInfo.isAdmin || userInfo.roles.includes(role)); }; // checks, if a String is proper JSON export const isJson = (text: string) => { - try { - const json = JSON.parse(text); - const type = Object.prototype.toString.call(json); - return type === "[object Object]" || type === "[object Array]"; - } catch (e) { - return false; - } + try { + const json = JSON.parse(text); + const type = Object.prototype.toString.call(json); + return type === "[object Object]" || type === "[object Array]"; + } catch (e) { + return false; + } }; /** @@ -125,23 +125,23 @@ export const isJson = (text: string) => { * suffix further specifies the asset value if necessary, e.g. "SHORT" for "displayOverride.SHORT" */ export const translateOverrideFallback = (asset: UploadAssetOption, t: TFunction, suffix?: "SHORT" | "DETAIL") => { - let result = undefined; - const sub = !!suffix ? `.${suffix}` as const : "" as const; - const translatable = asset["title"] + sub; + let result = undefined; + const sub = !!suffix ? `.${suffix}` as const : "" as const; + const translatable = asset["title"] + sub; - if (asset[`displayOverride${sub}` as const]) { - result = asset[`displayOverride${sub}` as const]; + if (asset[`displayOverride${sub}` as const]) { + result = asset[`displayOverride${sub}` as const]; - } else if (i18n.exists(translatable)) { - result = t(translatable); + } else if (i18n.exists(translatable)) { + result = t(translatable); - } else if (asset[`displayFallback${sub}` as const]) { - result = asset[`displayFallback${sub}` as const]; + } else if (asset[`displayFallback${sub}` as const]) { + result = asset[`displayFallback${sub}` as const]; - } else { - // no translate, override, or fallback, use what is given - result = translatable; - } + } else { + // no translate, override, or fallback, use what is given + result = translatable; + } - return result; + return result; } diff --git a/src/utils/validate.ts b/src/utils/validate.ts index fc8f6021b8..4558861ef7 100644 --- a/src/utils/validate.ts +++ b/src/utils/validate.ts @@ -12,221 +12,221 @@ today.setHours(0, 0, 0, 0); * Dynamically create a schema for a required metadata field */ export function createMetadataSchema( - schema: { [key: string]: any; }, - config: { id: string; required: boolean; type: string; }, + schema: { [key: string]: any; }, + config: { id: string; required: boolean; type: string; }, ) { - const { id, required, type } = config; - if (!required) return schema; - - let validationType: "string" | "array" | "date" = "string"; - const validations: { - type: string, - params: any[], - }[] = [ - { - type: "required", - params: ["this field is required"] - }, - ] - - if (type === "mixed_text") { - validationType = "array"; - validations.push({ - type: "min", - params: [1, "there should be atleast one entry"] - }) - } - - if (type === "date" || type === "start_date") { - validationType = "date"; - } - - if (!Yup[validationType as keyof typeof Yup]) { - return schema; - } - let validator = Yup[validationType as "string"](); - validations.forEach(validation => { - const { params, type } = validation; - // @ts-expect-error - if (!validator[type]) { - return; - } - // @ts-expect-error - validator = validator[type](...params); - }); - schema[id] = validator; - return schema; + const { id, required, type } = config; + if (!required) return schema; + + let validationType: "string" | "array" | "date" = "string"; + const validations: { + type: string, + params: any[], + }[] = [ + { + type: "required", + params: ["this field is required"] + }, + ] + + if (type === "mixed_text") { + validationType = "array"; + validations.push({ + type: "min", + params: [1, "there should be atleast one entry"] + }) + } + + if (type === "date" || type === "start_date") { + validationType = "date"; + } + + if (!Yup[validationType as keyof typeof Yup]) { + return schema; + } + let validator = Yup[validationType as "string"](); + validations.forEach(validation => { + const { params, type } = validation; + // @ts-expect-error + if (!validator[type]) { + return; + } + // @ts-expect-error + validator = validator[type](...params); + }); + schema[id] = validator; + return schema; } /** * Dynamically create a schema for required metadata fields */ export const MetadataSchema = (catalog: MetadataCatalog) => { - const schema = catalog.fields.reduce(createMetadataSchema, {}); - const schemaKeyReplace: { [key: string]: any} = {}; - for (const [key, value] of Object.entries(schema)) { - schemaKeyReplace[catalog.flavor + "_" + key] = value - } - const validateSchema = Yup.object().shape(schemaKeyReplace); - - return validateSchema; + const schema = catalog.fields.reduce(createMetadataSchema, {}); + const schemaKeyReplace: { [key: string]: any} = {}; + for (const [key, value] of Object.entries(schema)) { + schemaKeyReplace[catalog.flavor + "_" + key] = value + } + const validateSchema = Yup.object().shape(schemaKeyReplace); + + return validateSchema; } // Validation Schema used in new event wizard (each step has its own yup validation object) export const NewEventSchema = [ - Yup.object().shape({}), - Yup.object().shape({}), - Yup.object().shape({ - uploadAssetsTrack: Yup.array().when("sourceMode", { - is: (value: string) => value === "UPLOAD", - then: () => Yup.array().test( - "at-least-one-uploaded", - "at least one uploaded", - (uploadAssetsTrack) => { - return uploadAssetsTrack && uploadAssetsTrack.some((asset) => !!asset.file); - } - ), - }), - scheduleStartDate: Yup.date().when("sourceMode", { - is: (value: string) => - value === "SCHEDULE_SINGLE" || value === "SCHEDULE_MULTIPLE", - then: () => Yup.date().required("Required"), - }), - scheduleEndDate: Yup.date().when("sourceMode", { - is: "SCHEDULE_MULTIPLE", - then: () => Yup.date().required("Required"), - }), - repeatOn: Yup.array().when("sourceMode", { - is: "SCHEDULE_MULTIPLE", - then: () => Yup.array().min(1).required("Required"), - }), - scheduleStartHour: Yup.string().when("sourceMode", { - is: (value: string) => - value === "SCHEDULE_SINGLE" || value === "SCHEDULE_MULTIPLE", - then: () => Yup.string().required("Required"), - }), - scheduleStartMinute: Yup.string().when("sourceMode", { - is: (value: string) => - value === "SCHEDULE_SINGLE" || value === "SCHEDULE_MULTIPLE", - then: () => Yup.string().required("Required"), - }), - scheduleDurationHours: Yup.string().when("sourceMode", { - is: (value: string) => - value === "SCHEDULE_SINGLE" || value === "SCHEDULE_MULTIPLE", - then: () => Yup.string().required("Required"), - }), - scheduleDurationMinutes: Yup.string().when("sourceMode", { - is: (value: string) => - value === "SCHEDULE_SINGLE" || value === "SCHEDULE_MULTIPLE", - then: () => Yup.string().required("Required"), - }), - scheduleEndHour: Yup.string().when("sourceMode", { - is: (value: string) => - value === "SCHEDULE_SINGLE" || value === "SCHEDULE_MULTIPLE", - then: () => Yup.string().required("Required"), - }), - scheduleEndMinute: Yup.string().when("sourceMode", { - is: (value: string) => - value === "SCHEDULE_SINGLE" || value === "SCHEDULE_MULTIPLE", - then: () => Yup.string().required("Required"), - }), - location: Yup.string().when("sourceMode", { - is: (value: string) => - value === "SCHEDULE_SINGLE" || value === "SCHEDULE_MULTIPLE", - then: () => Yup.string().required("Required"), - }), - }), - Yup.object().shape({}), - Yup.object().shape({ - processingWorkflow: Yup.string().required("Required"), - }), + Yup.object().shape({}), + Yup.object().shape({}), + Yup.object().shape({ + uploadAssetsTrack: Yup.array().when("sourceMode", { + is: (value: string) => value === "UPLOAD", + then: () => Yup.array().test( + "at-least-one-uploaded", + "at least one uploaded", + (uploadAssetsTrack) => { + return uploadAssetsTrack && uploadAssetsTrack.some((asset) => !!asset.file); + } + ), + }), + scheduleStartDate: Yup.date().when("sourceMode", { + is: (value: string) => + value === "SCHEDULE_SINGLE" || value === "SCHEDULE_MULTIPLE", + then: () => Yup.date().required("Required"), + }), + scheduleEndDate: Yup.date().when("sourceMode", { + is: "SCHEDULE_MULTIPLE", + then: () => Yup.date().required("Required"), + }), + repeatOn: Yup.array().when("sourceMode", { + is: "SCHEDULE_MULTIPLE", + then: () => Yup.array().min(1).required("Required"), + }), + scheduleStartHour: Yup.string().when("sourceMode", { + is: (value: string) => + value === "SCHEDULE_SINGLE" || value === "SCHEDULE_MULTIPLE", + then: () => Yup.string().required("Required"), + }), + scheduleStartMinute: Yup.string().when("sourceMode", { + is: (value: string) => + value === "SCHEDULE_SINGLE" || value === "SCHEDULE_MULTIPLE", + then: () => Yup.string().required("Required"), + }), + scheduleDurationHours: Yup.string().when("sourceMode", { + is: (value: string) => + value === "SCHEDULE_SINGLE" || value === "SCHEDULE_MULTIPLE", + then: () => Yup.string().required("Required"), + }), + scheduleDurationMinutes: Yup.string().when("sourceMode", { + is: (value: string) => + value === "SCHEDULE_SINGLE" || value === "SCHEDULE_MULTIPLE", + then: () => Yup.string().required("Required"), + }), + scheduleEndHour: Yup.string().when("sourceMode", { + is: (value: string) => + value === "SCHEDULE_SINGLE" || value === "SCHEDULE_MULTIPLE", + then: () => Yup.string().required("Required"), + }), + scheduleEndMinute: Yup.string().when("sourceMode", { + is: (value: string) => + value === "SCHEDULE_SINGLE" || value === "SCHEDULE_MULTIPLE", + then: () => Yup.string().required("Required"), + }), + location: Yup.string().when("sourceMode", { + is: (value: string) => + value === "SCHEDULE_SINGLE" || value === "SCHEDULE_MULTIPLE", + then: () => Yup.string().required("Required"), + }), + }), + Yup.object().shape({}), + Yup.object().shape({ + processingWorkflow: Yup.string().required("Required"), + }), ]; // Validation Schema used in new series wizard (each step has its own yup validation object) export const NewSeriesSchema = [ - Yup.object().shape({ - title: Yup.string().required("Required"), - }), + Yup.object().shape({ + title: Yup.string().required("Required"), + }), ]; // Validation Schema used in new themes wizard (each step has its own yup validation object) export const NewThemeSchema = [ - Yup.object().shape({ - name: Yup.string().required("Required"), - }), - Yup.object().shape({ - bumperFile: Yup.string().when("bumperActive", { - is: true, - then: () => Yup.string().required("Required"), - }), - }), - Yup.object().shape({ - trailerFile: Yup.string().when("trailerActive", { - is: true, - then: () => Yup.string().required("Required"), - }), - }), - Yup.object().shape({ - titleSlideBackground: Yup.string().when("titleSlideMode", { - is: "upload", - then: () => Yup.string().required("Required"), - }), - }), - Yup.object().shape({ - watermarkFile: Yup.string().when("watermarkActive", { - is: true, - then: () => Yup.string().required("Required"), - }), - }), + Yup.object().shape({ + name: Yup.string().required("Required"), + }), + Yup.object().shape({ + bumperFile: Yup.string().when("bumperActive", { + is: true, + then: () => Yup.string().required("Required"), + }), + }), + Yup.object().shape({ + trailerFile: Yup.string().when("trailerActive", { + is: true, + then: () => Yup.string().required("Required"), + }), + }), + Yup.object().shape({ + titleSlideBackground: Yup.string().when("titleSlideMode", { + is: "upload", + then: () => Yup.string().required("Required"), + }), + }), + Yup.object().shape({ + watermarkFile: Yup.string().when("watermarkActive", { + is: true, + then: () => Yup.string().required("Required"), + }), + }), ]; // Validation Schema used in new ACL wizard (each step has its own yup validation object) export const NewAclSchema = [ - Yup.object().shape({ - name: Yup.string().required("Required"), - }), + Yup.object().shape({ + name: Yup.string().required("Required"), + }), ]; // Validation Schema used in new groups wizard (each step has its own yup validation object) export const NewGroupSchema = [ - Yup.object().shape({ - name: Yup.string().required("Required"), - }), + Yup.object().shape({ + name: Yup.string().required("Required"), + }), ]; // Validation Schema used in new user wizard export const NewUserSchema = (usernames: string[]) => - Yup.object().shape({ - username: Yup.string() - .required("Required") - .notOneOf(usernames, "not unique"), - name: Yup.string().required("Required"), - email: Yup.string().email().required("Required"), - password: Yup.string().required("Required"), - passwordConfirmation: Yup.string() - .oneOf([Yup.ref("password"), undefined], "Passwords must match") - .required("Required"), - }); + Yup.object().shape({ + username: Yup.string() + .required("Required") + .notOneOf(usernames, "not unique"), + name: Yup.string().required("Required"), + email: Yup.string().email().required("Required"), + password: Yup.string().required("Required"), + passwordConfirmation: Yup.string() + .oneOf([Yup.ref("password"), undefined], "Passwords must match") + .required("Required"), + }); // Validation Schema used in user details modal export const EditUserSchema = Yup.object().shape({ - name: Yup.string().required("Required"), - email: Yup.string().email().required("Required"), - passwordConfirmation: Yup.string().when("password", { - is: (value: any) => !!value, - then: () => Yup.string() - .oneOf([Yup.ref("password"), undefined], "Passwords must match") - .required("Required"), - }), + name: Yup.string().required("Required"), + email: Yup.string().email().required("Required"), + passwordConfirmation: Yup.string().when("password", { + is: (value: any) => !!value, + then: () => Yup.string() + .oneOf([Yup.ref("password"), undefined], "Passwords must match") + .required("Required"), + }), }); // Validation Schema used in group details modal export const EditGroupSchema = Yup.object().shape({ - name: Yup.string().required("Required"), + name: Yup.string().required("Required"), }); // Validation Schema used in adopter registration modal export const AdopterRegistrationSchema = Yup.object().shape({ - email: Yup.string().email(), + email: Yup.string().email(), }); diff --git a/src/utils/wizardUtils.ts b/src/utils/wizardUtils.ts index b906ddf664..191785aac0 100644 --- a/src/utils/wizardUtils.ts +++ b/src/utils/wizardUtils.ts @@ -1,33 +1,33 @@ // Base style for Stepper component export const stepperStyle = { - root: { - background: "#eeeff0", - height: "100px", - padding: "24px", - }, + root: { + background: "#eeeff0", + height: "100px", + padding: "24px", + }, }; // Properly align multi-line wizard step labels export const stepLabelStyle = { - root: { - alignSelf: "flex-start", - } + root: { + alignSelf: "flex-start", + } }; // Style of icons used in Stepper export const stepIcon = { - root: { - height: 22, - alignItems: "center", - }, - circle: { - color: "#92a0ab", - width: "20px", - height: "20px", - }, - circleActive: { - transform: "scale(1.3)" - }, + root: { + height: 22, + alignItems: "center", + }, + circle: { + color: "#92a0ab", + width: "20px", + height: "20px", + }, + circleActive: { + transform: "scale(1.3)" + }, }; /* This method checks if the summary page is reachable. @@ -37,18 +37,18 @@ export const stepIcon = { */ export const isSummaryReachable = ( - key: number, - steps: { - name: string, - hidden?: boolean, - }[], - completed: Record, + key: number, + steps: { + name: string, + hidden?: boolean, + }[], + completed: Record, ) => { - if (steps[key].name === "summary") { - const visibleSteps = steps.filter((step) => !step.hidden); + if (steps[key].name === "summary") { + const visibleSteps = steps.filter((step) => !step.hidden); - return Object.keys(completed).length >= visibleSteps.length - 2; - } + return Object.keys(completed).length >= visibleSteps.length - 2; + } - return true; + return true; }; diff --git a/src/utils/workflowPanelUtils.ts b/src/utils/workflowPanelUtils.ts index 6017c5a153..1686cef114 100644 --- a/src/utils/workflowPanelUtils.ts +++ b/src/utils/workflowPanelUtils.ts @@ -2,36 +2,36 @@ import { Workflow, FieldSetField } from "../slices/workflowSlice"; // fill values with default configuration of chosen workflow export const setDefaultConfig = (workflowDefinitions: Workflow[], workflowId: string) => { - let defaultConfiguration: { [key: string]: unknown } = {}; - - // find configuration panel information about chosen workflow - let configPanel = workflowDefinitions.find( - (workflow) => workflow.id === workflowId - )?.configuration_panel_json; - - // only set default values if there is an configuration panel - if (Array.isArray(configPanel) && configPanel.length > 0) { - // iterate through all config options and set their defaults - configPanel.forEach((configOption) => { - if (configOption.fieldset) { - defaultConfiguration = fillDefaultConfig( - configOption.fieldset, - defaultConfiguration - ); - } - }); - } - - return defaultConfiguration; + let defaultConfiguration: { [key: string]: unknown } = {}; + + // find configuration panel information about chosen workflow + let configPanel = workflowDefinitions.find( + (workflow) => workflow.id === workflowId + )?.configuration_panel_json; + + // only set default values if there is an configuration panel + if (Array.isArray(configPanel) && configPanel.length > 0) { + // iterate through all config options and set their defaults + configPanel.forEach((configOption) => { + if (configOption.fieldset) { + defaultConfiguration = fillDefaultConfig( + configOption.fieldset, + defaultConfiguration + ); + } + }); + } + + return defaultConfiguration; }; // fills default configuration with values const fillDefaultConfig = ( - fieldset: FieldSetField[], - defaultConfiguration: { [key: string]: unknown } + fieldset: FieldSetField[], + defaultConfiguration: { [key: string]: unknown } ) => { - // iteration through each input field - fieldset.forEach((field) => { + // iteration through each input field + fieldset.forEach((field) => { // set only the checked input of radio button as default value if (field.type === "radio" && field.checked) { @@ -47,14 +47,14 @@ const fillDefaultConfig = ( defaultConfiguration[field.name] = field.value; } - // if an input has further configuration then go through fillDefaultConfig again - if (field.fieldset) { - defaultConfiguration = fillDefaultConfig( - field.fieldset, - defaultConfiguration - ); - } - }); + // if an input has further configuration then go through fillDefaultConfig again + if (field.fieldset) { + defaultConfiguration = fillDefaultConfig( + field.fieldset, + defaultConfiguration + ); + } + }); - return defaultConfiguration; + return defaultConfiguration; }; diff --git a/test/POST/admin-ng/themes b/test/POST/admin-ng/themes index ffe406b73c..5398799689 100644 --- a/test/POST/admin-ng/themes +++ b/test/POST/admin-ng/themes @@ -6,9 +6,9 @@ "description":"Private Theme of Entwine", "name":"Theme Entwine Private", "creator":{ - "username":"admin", - "email":"", - "name":"Admin User" + "username":"admin", + "email":"", + "name":"Admin User" }, "bumperActive":true, "bumperFile":"uuid1",