├── src ├── react-app-env.d.ts ├── assets │ └── images │ │ └── bg.jpg ├── store │ └── configureStore.ts ├── types │ ├── menubar.ts │ ├── chmodCalculator.ts │ ├── passwordGenerator.ts │ ├── epochConverter.ts │ └── crontabGenerator.ts ├── actions │ ├── CrontabGenerator │ │ ├── generateCrontabLine.ts │ │ ├── commandToExecute.ts │ │ ├── Schedule │ │ │ ├── days.ts │ │ │ ├── hours.ts │ │ │ ├── months.ts │ │ │ ├── minutes.ts │ │ │ └── weekday.ts │ │ └── howToHandleOutput.ts │ ├── EpochConverter │ │ ├── unixTimeForm.ts │ │ ├── currentEpochTime.ts │ │ └── dateTimeForm.ts │ ├── ChmodCalculator │ │ └── chmodCalculator.ts │ └── PasswordGenerator │ │ └── passwordGenerator.ts ├── utils │ ├── validators │ │ ├── emailValidator.ts │ │ ├── passwordValidator.ts │ │ └── chmodValidator.ts │ ├── generators │ │ └── passwordGenerator.ts │ ├── calculators │ │ ├── epochConverter.ts │ │ └── chmodCalculator.ts │ └── transformers │ │ ├── map.json │ │ └── humanReadableToCrontab.ts ├── setupTests.ts ├── reducers │ ├── EpochConverter │ │ ├── index.ts │ │ ├── unixTimeForm.ts │ │ ├── currentEpochTime.ts │ │ └── dateTimeForm.ts │ ├── index.ts │ ├── CrontabGenerator │ │ ├── generateCrontabLine.ts │ │ ├── index.ts │ │ ├── commandToExecute.ts │ │ ├── Schedule │ │ │ ├── days.ts │ │ │ ├── hours.ts │ │ │ ├── months.ts │ │ │ ├── minutes.ts │ │ │ └── weekday.ts │ │ └── howToHandleOutput.ts │ ├── ChmodCalculator │ │ └── index.ts │ └── PasswordGenerator │ │ └── index.ts ├── tests │ ├── App.test.tsx │ ├── containers │ │ ├── EpochConverter │ │ │ ├── DateTimeForm.test.tsx │ │ │ ├── UnixTimeForm.test.tsx │ │ │ └── CurrentEpochTime.test.tsx │ │ ├── Menubar │ │ │ └── Menubar.test.tsx │ │ ├── CrontabGenerator │ │ │ ├── GenerateCrontabLine.test.tsx │ │ │ └── Schedule │ │ │ │ ├── Months.test.tsx │ │ │ │ ├── Days.test.tsx │ │ │ │ ├── Hours.test.tsx │ │ │ │ ├── Minutes.test.tsx │ │ │ │ └── Weekday.test.tsx │ │ ├── ChmodCalculator │ │ │ └── ChmodCalculator.test.tsx │ │ └── PasswordGenerator │ │ │ └── PasswordGenerator.test.tsx │ ├── components │ │ ├── common │ │ │ └── CopyToClipboard.test.tsx │ │ ├── PasswordGenerator │ │ │ ├── FormFields │ │ │ │ ├── PasswordLength.test.tsx │ │ │ │ ├── Checkbox.test.tsx │ │ │ │ └── Checkboxes.test.tsx │ │ │ ├── GenerateAndCopy.test.tsx │ │ │ └── PasswordGenerator.test.tsx │ │ ├── EpochConverter │ │ │ ├── CurrentEpochTime.test.tsx │ │ │ ├── Forms │ │ │ │ ├── UnixTimeForm.test.tsx │ │ │ │ └── DateTimeForm.test.tsx │ │ │ └── EpochConverter.test.tsx │ │ ├── CrontabGenerator │ │ │ ├── FormFields │ │ │ │ ├── HowToHandleOutput │ │ │ │ │ ├── Mute.test.tsx │ │ │ │ │ └── FileOrEmail.test.tsx │ │ │ │ └── Schedule │ │ │ │ │ └── Period.test.tsx │ │ │ └── CrontabGenerator.test.tsx │ │ ├── Menubar │ │ │ └── Menubar.test.tsx │ │ └── ChmodCalculator │ │ │ ├── Checkboxes │ │ │ ├── CheckboxGroup.test.tsx │ │ │ └── Checkbox.test.tsx │ │ │ └── ChmodCalculator..test.tsx │ ├── utils │ │ ├── validators │ │ │ ├── passwordValidator.test.ts │ │ │ ├── emailValidator.test.ts │ │ │ └── chmodValidator.test.ts │ │ ├── generators │ │ │ └── passwordGenerator.test.ts │ │ ├── calculators │ │ │ ├── chmodCalculator.test.ts │ │ │ └── epochConverter.test.ts │ │ └── transformers │ │ │ └── humanReadableToCrontab.test.ts │ ├── reducers │ │ ├── CrontabGenerator │ │ │ ├── generateCrontabLine.test.ts │ │ │ ├── commandToExecute.test.ts │ │ │ ├── Schedule │ │ │ │ ├── days.test.ts │ │ │ │ ├── hourss.test.ts │ │ │ │ ├── minutes.test.ts │ │ │ │ ├── months.test.ts │ │ │ │ └── weekday.test.ts │ │ │ └── howToHandleOutput.test.ts │ │ ├── EpochConverter │ │ │ ├── unixTimeForm.test.ts │ │ │ ├── currentEpochTime.test.ts │ │ │ └── dateTimeForm.test.ts │ │ ├── ChmodCalculator │ │ │ └── index.test.ts │ │ └── PasswordGenerator │ │ │ └── index.test.ts │ └── selectors │ │ └── crontabGenerator.test.ts ├── components │ ├── PasswordGenerator │ │ ├── FormFields │ │ │ ├── PasswordLength.tsx │ │ │ ├── Checkbox.tsx │ │ │ └── Checkboxes.tsx │ │ ├── GenerateAndCopy.tsx │ │ └── PasswordGenerator.tsx │ ├── EpochConverter │ │ ├── EpochConverter.tsx │ │ ├── CurrentEpochTime.tsx │ │ └── Forms │ │ │ ├── UnixTimeForm.tsx │ │ │ └── DateTimeForm.tsx │ ├── CrontabGenerator │ │ ├── FormFields │ │ │ ├── Schedule │ │ │ │ ├── RadioButton.tsx │ │ │ │ ├── RadioButtonList.tsx │ │ │ │ ├── RadioButtonWithMultiSelect.tsx │ │ │ │ └── Period.tsx │ │ │ ├── HowToHandleOutput │ │ │ │ ├── Mute.tsx │ │ │ │ └── FileOrEmail.tsx │ │ │ ├── HowToHandleOutput.tsx │ │ │ └── CommandToExecute.tsx │ │ ├── GenerateCrontabLine.tsx │ │ └── CrontabGenerator.tsx │ ├── ChmodCalculator │ │ ├── Checkboxes │ │ │ ├── CheckboxGroup.tsx │ │ │ ├── Checkbox.tsx │ │ │ └── Checkboxes.tsx │ │ └── ChmodCalculator.tsx │ ├── Menubar │ │ └── Menubar.tsx │ └── common │ │ └── CopyToClipboard.tsx ├── index.css ├── App.css ├── index.tsx ├── containers │ ├── Menubar │ │ └── Menubar.tsx │ ├── CrontabGenerator │ │ ├── CommandToExecute.tsx │ │ ├── HowToHandleOutput.tsx │ │ ├── Schedule │ │ │ ├── Days.tsx │ │ │ ├── Weekday.tsx │ │ │ ├── Minutes.tsx │ │ │ ├── Months.tsx │ │ │ └── Hours.tsx │ │ └── GenerateCrontabLine.tsx │ ├── EpochConverter │ │ ├── UnixTimeForm.tsx │ │ ├── CurrentEpochTime.tsx │ │ └── DateTimeForm.tsx │ ├── ChmodCalculator │ │ └── ChmodCalculator.tsx │ └── PasswordGenerator │ │ └── PasswordGenerator.tsx ├── App.tsx ├── selectors │ └── crontabGenerator.js ├── constants │ └── ActionTypes.ts └── serviceWorker.ts ├── .huskyrc.json ├── public ├── robots.txt ├── favicon.ico ├── logo192.png ├── logo512.png ├── manifest.json └── index.html ├── .lintstagedrc.json ├── .editorconfig ├── .gitignore ├── tsconfig.json ├── .github └── workflows │ ├── node.js.yml │ └── codeql-analysis.yml ├── LICENSE ├── README.md ├── .eslintrc └── package.json /src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /.huskyrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "hooks": { 3 | "pre-commit": "lint-staged" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Coderberg/web-developer-tools/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Coderberg/web-developer-tools/HEAD/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Coderberg/web-developer-tools/HEAD/public/logo512.png -------------------------------------------------------------------------------- /src/assets/images/bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Coderberg/web-developer-tools/HEAD/src/assets/images/bg.jpg -------------------------------------------------------------------------------- /.lintstagedrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "*.+(js|jsx)": ["eslint --fix", "git add"], 3 | "*.+(json|css|md)": ["prettier --write", "git add"] 4 | } 5 | -------------------------------------------------------------------------------- /src/store/configureStore.ts: -------------------------------------------------------------------------------- 1 | import {configureStore} from '@reduxjs/toolkit' 2 | import rootReducer from "../reducers"; 3 | 4 | const store = configureStore({ 5 | reducer: rootReducer 6 | }) 7 | 8 | export default store 9 | -------------------------------------------------------------------------------- /src/types/menubar.ts: -------------------------------------------------------------------------------- 1 | import {MouseEventHandler} from "react"; 2 | 3 | export type MenubarPropsType = { 4 | model: Array, 5 | onLogoClick: MouseEventHandler, 6 | onGitHubClick: MouseEventHandler 7 | } 8 | -------------------------------------------------------------------------------- /src/actions/CrontabGenerator/generateCrontabLine.ts: -------------------------------------------------------------------------------- 1 | import {GENERATE_CRONTAB_LINE} from "../../constants/ActionTypes"; 2 | 3 | export const setResult = (payload: string) => ({ 4 | type: GENERATE_CRONTAB_LINE, payload 5 | }); 6 | 7 | export default setResult; 8 | -------------------------------------------------------------------------------- /src/utils/validators/emailValidator.ts: -------------------------------------------------------------------------------- 1 | const isEmailValid = (email: string): boolean => { 2 | const pattern = /^[a-z0-9_.-]+@[a-z0-9-]+\.([a-z]{1,6}\.)?[a-z]{2,6}$/i; 3 | 4 | return email.search(pattern) === 0; 5 | } 6 | 7 | export default isEmailValid; 8 | -------------------------------------------------------------------------------- /src/setupTests.ts: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom/extend-expect'; 6 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | end_of_line = lf 7 | indent_size = 4 8 | indent_style = space 9 | insert_final_newline = true 10 | max_line_length = 80 11 | trim_trailing_whitespace = true 12 | 13 | [*.md] 14 | max_line_length = 0 15 | trim_trailing_whitespace = false 16 | -------------------------------------------------------------------------------- /src/utils/validators/passwordValidator.ts: -------------------------------------------------------------------------------- 1 | import {CheckboxesType} from "../../types/passwordGenerator"; 2 | 3 | const isChangeAllowed = (checked: CheckboxesType): boolean => 4 | !(!checked.numbers 5 | && !checked.symbols 6 | && !checked.lowercase 7 | && !checked.uppercase) 8 | 9 | export default isChangeAllowed; 10 | -------------------------------------------------------------------------------- /src/reducers/EpochConverter/index.ts: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux' 2 | import unixTimeForm from "./unixTimeForm"; 3 | import dateTimeForm from "./dateTimeForm"; 4 | import currentEpochTime from "./currentEpochTime"; 5 | 6 | const epochConverter = combineReducers({ 7 | unixTimeForm, 8 | dateTimeForm, 9 | currentEpochTime, 10 | }) 11 | 12 | export default epochConverter 13 | -------------------------------------------------------------------------------- /src/actions/CrontabGenerator/commandToExecute.ts: -------------------------------------------------------------------------------- 1 | import { 2 | SET_COMMAND_TO_EXECUTE, 3 | SET_COMMAND_HAS_ERROR 4 | } from "../../constants/ActionTypes"; 5 | 6 | export const setCommandToExecuteValue = (payload: string) => ({ 7 | type: SET_COMMAND_TO_EXECUTE, payload 8 | }); 9 | 10 | export const setCommandHasError = (payload: boolean) => ({ 11 | type: SET_COMMAND_HAS_ERROR, payload 12 | }); 13 | -------------------------------------------------------------------------------- /src/actions/EpochConverter/unixTimeForm.ts: -------------------------------------------------------------------------------- 1 | import { 2 | SET_EPOCH_TO_DATE_RESULT, 3 | SET_UNIX_TIME_INPUT_VALUE 4 | } from "../../constants/ActionTypes"; 5 | 6 | export const setUnixTimeInputValue = (payload: string) => ({ 7 | type: SET_UNIX_TIME_INPUT_VALUE, payload 8 | }); 9 | 10 | export const setEpochToDateResult = (payload: string) => ({ 11 | type: SET_EPOCH_TO_DATE_RESULT, payload 12 | }); 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | /.idea 4 | 5 | # dependencies 6 | /node_modules 7 | /.pnp 8 | .pnp.js 9 | 10 | # testing 11 | /coverage 12 | 13 | # production 14 | /build 15 | 16 | # misc 17 | .DS_Store 18 | .env.local 19 | .env.development.local 20 | .env.test.local 21 | .env.production.local 22 | 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | -------------------------------------------------------------------------------- /src/actions/EpochConverter/currentEpochTime.ts: -------------------------------------------------------------------------------- 1 | import { 2 | SET_CURRENT_UNIX_TIME, 3 | SET_COPY_TO_CLIPBOARD_BUTTON_STATE 4 | } from "../../constants/ActionTypes"; 5 | 6 | export const setCurrentUnixTime = (payload: string) => ({ 7 | type: SET_CURRENT_UNIX_TIME, payload 8 | }); 9 | 10 | export const setCopyToClipboardButtonState = (payload: 'default' | 'pressed') => ({ 11 | type: SET_COPY_TO_CLIPBOARD_BUTTON_STATE, payload 12 | }); 13 | -------------------------------------------------------------------------------- /src/reducers/index.ts: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux' 2 | import epochConverter from "./EpochConverter"; 3 | import chmodCalculator from "./ChmodCalculator"; 4 | import crontabGenerator from "./CrontabGenerator"; 5 | import passwordGenerator from "./PasswordGenerator"; 6 | 7 | const rootReducer = combineReducers({ 8 | chmodCalculator, 9 | epochConverter, 10 | crontabGenerator, 11 | passwordGenerator 12 | }) 13 | 14 | export default rootReducer 15 | -------------------------------------------------------------------------------- /src/tests/App.test.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { render, screen } from '@testing-library/react'; 3 | import {Provider} from "react-redux"; 4 | import App from "../App"; 5 | import store from "../store/configureStore"; 6 | 7 | test('renders Chmod Calculator form', () => { 8 | render(); 9 | const element = screen.getAllByText(/Chmod Calculator/i); 10 | expect(element[1]).toBeInTheDocument(); 11 | }); 12 | -------------------------------------------------------------------------------- /src/components/PasswordGenerator/FormFields/PasswordLength.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import {Slider} from "primereact/slider"; 3 | 4 | const PasswordLength = ({passwordLength, onPasswordLengthChange}: any) => 5 | 6 |
7 |

Password Length: {passwordLength}

8 | 11 |
12 | 13 | export default PasswordLength; 14 | -------------------------------------------------------------------------------- /src/actions/CrontabGenerator/Schedule/days.ts: -------------------------------------------------------------------------------- 1 | import { 2 | SET_DAYS_RADIO_BUTTON_VALUE, 3 | SET_DAYS_OPTION_VALUE, 4 | SET_DAY_HAS_ERROR 5 | } from "../../../constants/ActionTypes"; 6 | 7 | export const setDaysRadioButtonValue = (payload: string) => ({ 8 | type: SET_DAYS_RADIO_BUTTON_VALUE, payload 9 | }); 10 | 11 | export const setDaysOptionValue = (payload: any[]) => ({ 12 | type: SET_DAYS_OPTION_VALUE, payload 13 | }); 14 | 15 | export const setDayHasError = (payload: boolean) => ({ 16 | type: SET_DAY_HAS_ERROR, payload 17 | }); 18 | -------------------------------------------------------------------------------- /src/utils/generators/passwordGenerator.ts: -------------------------------------------------------------------------------- 1 | import {CheckboxesType} from "../../types/passwordGenerator"; 2 | 3 | const generator = require('generate-password-browser'); 4 | 5 | const generatePassword = (length: string, options: CheckboxesType) => generator.generate({ 6 | length: parseInt(length, 10), 7 | numbers: options.numbers, 8 | symbols: options.symbols, 9 | lowercase: options.lowercase, 10 | uppercase: options.uppercase, 11 | excludeSimilarCharacters: options.excludeSimilar 12 | }) 13 | 14 | export default generatePassword; 15 | -------------------------------------------------------------------------------- /src/actions/CrontabGenerator/howToHandleOutput.ts: -------------------------------------------------------------------------------- 1 | import { 2 | HOW_TO_HANDLE_OUTPUT, 3 | SET_PATH_TO_FILE_VALUE, 4 | SET_EMAIL_VALUE 5 | } from "../../constants/ActionTypes"; 6 | 7 | export const setHowToHandleOutputValue = (payload: 'mute' | 'file' | 'email') => ({ 8 | type: HOW_TO_HANDLE_OUTPUT, payload 9 | }); 10 | 11 | export const setPathToFileValue = (payload: string) => ({ 12 | type: SET_PATH_TO_FILE_VALUE, payload 13 | }); 14 | 15 | export const setEmailValue = (payload: string) => ({ 16 | type: SET_EMAIL_VALUE, payload 17 | }); 18 | -------------------------------------------------------------------------------- /src/actions/CrontabGenerator/Schedule/hours.ts: -------------------------------------------------------------------------------- 1 | import { 2 | SET_HOURS_RADIO_BUTTON_VALUE, 3 | SET_HOURS_OPTION_VALUE, 4 | SET_HOUR_HAS_ERROR 5 | } from "../../../constants/ActionTypes"; 6 | 7 | export const setHoursRadioButtonValue = (payload: string) => ({ 8 | type: SET_HOURS_RADIO_BUTTON_VALUE, payload 9 | }); 10 | 11 | export const setHoursOptionValue = (payload: any[]) => ({ 12 | type: SET_HOURS_OPTION_VALUE, payload 13 | }); 14 | 15 | export const setHourHasError = (payload: boolean) => ({ 16 | type: SET_HOUR_HAS_ERROR, payload 17 | }); 18 | -------------------------------------------------------------------------------- /src/actions/CrontabGenerator/Schedule/months.ts: -------------------------------------------------------------------------------- 1 | import { 2 | SET_MONTHS_RADIO_BUTTON_VALUE, 3 | SET_MONTHS_OPTION_VALUE, 4 | SET_MONTH_HAS_ERROR 5 | } from "../../../constants/ActionTypes"; 6 | 7 | export const setMonthsRadioButtonValue = (payload: string) => ({ 8 | type: SET_MONTHS_RADIO_BUTTON_VALUE, payload 9 | }); 10 | 11 | export const setMonthsOptionValue = (payload: any[]) => ({ 12 | type: SET_MONTHS_OPTION_VALUE, payload 13 | }); 14 | 15 | export const setMonthHasError = (payload: boolean) => ({ 16 | type: SET_MONTH_HAS_ERROR, payload 17 | }); 18 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | background-color: #121212; 9 | background-image: url('assets/images/bg.jpg'); 10 | background-attachment: fixed; 11 | background-size: cover; 12 | } 13 | 14 | code { 15 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 16 | monospace; 17 | } 18 | -------------------------------------------------------------------------------- /src/actions/CrontabGenerator/Schedule/minutes.ts: -------------------------------------------------------------------------------- 1 | import { 2 | SET_MINUTES_RADIO_BUTTON_VALUE, 3 | SET_MINUTES_OPTION_VALUE, 4 | SET_MINUTE_HAS_ERROR 5 | } from "../../../constants/ActionTypes"; 6 | 7 | export const setMinutesRadioButtonValue = (payload: string) => ({ 8 | type: SET_MINUTES_RADIO_BUTTON_VALUE, payload 9 | }); 10 | 11 | export const setMinutesOptionValue = (payload: any[]) => ({ 12 | type: SET_MINUTES_OPTION_VALUE, payload 13 | }); 14 | 15 | export const setMinuteHasError = (payload: boolean) => ({ 16 | type: SET_MINUTE_HAS_ERROR, payload 17 | }); 18 | -------------------------------------------------------------------------------- /src/actions/CrontabGenerator/Schedule/weekday.ts: -------------------------------------------------------------------------------- 1 | import { 2 | SET_WEEKDAY_RADIO_BUTTON_VALUE, 3 | SET_WEEKDAY_OPTION_VALUE, 4 | SET_WEEKDAY_HAS_ERROR 5 | } from "../../../constants/ActionTypes"; 6 | 7 | export const setWeekdayRadioButtonValue = (payload: string) => ({ 8 | type: SET_WEEKDAY_RADIO_BUTTON_VALUE, payload 9 | }); 10 | 11 | export const setWeekdayOptionValue = (payload: any[]) => ({ 12 | type: SET_WEEKDAY_OPTION_VALUE, payload 13 | }); 14 | 15 | export const setWeekdayHasError = (payload: boolean) => ({ 16 | type: SET_WEEKDAY_HAS_ERROR, payload 17 | }); 18 | -------------------------------------------------------------------------------- /src/actions/EpochConverter/dateTimeForm.ts: -------------------------------------------------------------------------------- 1 | import { 2 | SET_DATE_TIME_INPUT_VALUE, 3 | SET_DATE_TIMEZONE_INPUT_VALUE, 4 | SET_DATE_TO_EPOCH_RESULT 5 | } from "../../constants/ActionTypes"; 6 | 7 | export const setDateTimeInputValue = (payload: Date) => ({ 8 | type: SET_DATE_TIME_INPUT_VALUE, payload 9 | }); 10 | 11 | export const setTimezoneInputValue = (payload: 'local' | 'gmt') => ({ 12 | type: SET_DATE_TIMEZONE_INPUT_VALUE, payload 13 | }); 14 | 15 | export const setDateToEpochResult = (payload: string) => ({ 16 | type: SET_DATE_TO_EPOCH_RESULT, payload 17 | }); 18 | -------------------------------------------------------------------------------- /src/actions/ChmodCalculator/chmodCalculator.ts: -------------------------------------------------------------------------------- 1 | import { 2 | SET_CHECKED, 3 | SET_NUMBER_FIELD_VALUE, 4 | SET_TEXT_FIELD_VALUE 5 | } from "../../constants/ActionTypes"; 6 | import {CheckboxesType} from "../../types/chmodCalculator"; 7 | 8 | export const setChecked = (payload: CheckboxesType) => ({ 9 | type: SET_CHECKED, payload 10 | }); 11 | 12 | export const setNumberFieldValue = (payload: string) => ({ 13 | type: SET_NUMBER_FIELD_VALUE, payload 14 | }); 15 | 16 | export const setTextFieldValue = (payload: string) => ({ 17 | type: SET_TEXT_FIELD_VALUE, payload 18 | }); 19 | -------------------------------------------------------------------------------- /src/reducers/CrontabGenerator/generateCrontabLine.ts: -------------------------------------------------------------------------------- 1 | import {GENERATE_CRONTAB_LINE,} from "../../constants/ActionTypes"; 2 | 3 | const initialState = { 4 | result: '', 5 | } 6 | 7 | const generateCrontabLine = (state = initialState, action: { type: string; payload: string; }) => { 8 | 9 | switch (action.type) { 10 | 11 | case GENERATE_CRONTAB_LINE: { 12 | return { 13 | ...state, result: action.payload 14 | }; 15 | } 16 | default: 17 | return state; 18 | } 19 | } 20 | 21 | export default generateCrontabLine; 22 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "Dev Tools", 3 | "name": "Free Online Developer Tools", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /src/tests/containers/EpochConverter/DateTimeForm.test.tsx: -------------------------------------------------------------------------------- 1 | import {fireEvent, render, screen} from "@testing-library/react"; 2 | import React from "react"; 3 | import {Provider} from "react-redux"; 4 | import DateTimeForm from "../../../containers/EpochConverter/DateTimeForm"; 5 | import store from "../../../store/configureStore"; 6 | 7 | test('Human date to Timestamp', () => { 8 | render(); 9 | const btn = screen.getByText("Human date to Timestamp"); 10 | fireEvent.click(btn); 11 | expect(screen.getByText(/Unix Timestamp/i)).toBeInTheDocument(); 12 | }); 13 | -------------------------------------------------------------------------------- /src/components/EpochConverter/EpochConverter.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import {Fieldset} from "primereact/fieldset"; 3 | import CurrentEpochTime from "../../containers/EpochConverter/CurrentEpochTime"; 4 | import UnixTimeForm from "../../containers/EpochConverter/UnixTimeForm"; 5 | import DateTimeForm from "../../containers/EpochConverter/DateTimeForm"; 6 | 7 | const EpochConverter: React.FC = () => 8 | 9 |
10 | 11 | 12 | 13 |
14 | 15 | export default EpochConverter; 16 | -------------------------------------------------------------------------------- /src/components/CrontabGenerator/FormFields/Schedule/RadioButton.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import {RadioButton as PrimeRadioButton} from "primereact/radiobutton"; 3 | 4 | const RadioButton = ({button, id, checked, onRadioButtonChange}: 5 | { button: string, id: string, checked: boolean, onRadioButtonChange: any }) => 6 | 7 |
8 | 10 | 11 |
12 | 13 | export default RadioButton; 14 | -------------------------------------------------------------------------------- /src/tests/containers/Menubar/Menubar.test.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import {render, screen} from "@testing-library/react"; 3 | import {HashRouter as Router} from 'react-router-dom'; 4 | import Menubar from "../../../containers/Menubar/Menubar"; 5 | 6 | const expectedMenuItems: string[] = [ 7 | 'Chmod Calculator', 8 | 'Crontab Generator', 9 | 'Unix Timestamp Converter', 10 | 'Password Generator', 11 | ]; 12 | 13 | render(); 14 | 15 | test('shows the menu items', () => { 16 | expectedMenuItems.forEach((item) => { 17 | expect(screen.getByText(item)).toBeInTheDocument() 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "strict": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "noFallthroughCasesInSwitch": true, 16 | "module": "esnext", 17 | "moduleResolution": "node", 18 | "resolveJsonModule": true, 19 | "isolatedModules": true, 20 | "noEmit": true, 21 | "jsx": "react-jsx" 22 | }, 23 | "include": [ 24 | "src" 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /src/tests/components/common/CopyToClipboard.test.tsx: -------------------------------------------------------------------------------- 1 | import {fireEvent, render, screen} from "@testing-library/react"; 2 | import React from "react"; 3 | import CopyToClipboard from "../../../components/common/CopyToClipboard"; 4 | 5 | const copyToClipboard = jest.fn(); 6 | 7 | const setup = () => { 8 | render() 9 | const button = screen.getByText('Copy') 10 | return {button} 11 | } 12 | 13 | it('should simulate button click', () => { 14 | const {button} = setup() 15 | fireEvent.click(button); 16 | expect(copyToClipboard).toBeCalledTimes(1) 17 | }); 18 | -------------------------------------------------------------------------------- /src/components/CrontabGenerator/FormFields/HowToHandleOutput/Mute.tsx: -------------------------------------------------------------------------------- 1 | import {RadioButton} from "primereact/radiobutton"; 2 | import React from "react"; 3 | 4 | const Mute = ({value, onChange}: { value: string, onChange: any }) => { 5 | 6 | const label = 'Mute the execution (Don\'t save or send output)'; 7 | 8 | return
9 | 10 | 15 | 16 | 17 |
18 | } 19 | 20 | export default Mute; 21 | -------------------------------------------------------------------------------- /src/components/PasswordGenerator/FormFields/Checkbox.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import {Checkbox as PrimeCheckbox} from "primereact/checkbox"; 3 | import {CheckboxPropsInterface} from "../../../types/passwordGenerator"; 4 | 5 | const Checkbox = ({ 6 | label, 7 | id, 8 | checked, 9 | onCheckboxChange 10 | }: CheckboxPropsInterface) => 11 | 12 |
13 | 15 | 16 |
17 | 18 | export default Checkbox; 19 | -------------------------------------------------------------------------------- /src/reducers/CrontabGenerator/index.ts: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux' 2 | import commandToExecute from "./commandToExecute"; 3 | import days from "./Schedule/days"; 4 | import generateCrontabLine from "./generateCrontabLine"; 5 | import hours from "./Schedule/hours"; 6 | import minutes from "./Schedule/minutes"; 7 | import months from "./Schedule/months"; 8 | import weekday from "./Schedule/weekday"; 9 | import howToHandleOutput from "./howToHandleOutput"; 10 | 11 | const crontabGenerator = combineReducers({ 12 | commandToExecute, 13 | generateCrontabLine, 14 | howToHandleOutput, 15 | days, 16 | hours, 17 | minutes, 18 | months, 19 | weekday 20 | }) 21 | 22 | export default crontabGenerator 23 | -------------------------------------------------------------------------------- /src/App.css: -------------------------------------------------------------------------------- 1 | .max-w-760 { 2 | max-width: 760px; 3 | margin: 0 auto; 4 | } 5 | 6 | .max-w-980 { 7 | max-width: 980px; 8 | margin: 0 auto; 9 | } 10 | 11 | .w-100 { 12 | width: 100% 13 | } 14 | 15 | .card { 16 | border: 1px solid transparent; 17 | } 18 | 19 | .card-invalid { 20 | border: 1px solid #ef9a9a; 21 | } 22 | 23 | .border-top { 24 | border-top: 1px solid #00000082; 25 | } 26 | 27 | code { 28 | color: #000; 29 | padding: 2px 10px; 30 | } 31 | 32 | .bg-wisteria { 33 | background-color: #ce93d8; 34 | } 35 | 36 | .p-fieldset { 37 | background: #1e1e1ef2 !important; 38 | } 39 | 40 | @media (max-width: 960px) { 41 | .logo { 42 | display: none !important; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/utils/validators/chmodValidator.ts: -------------------------------------------------------------------------------- 1 | export const isNumberValid = (value: string): boolean => { 2 | const pattern = /^[0-7]*$/; 3 | if (!pattern.test(value)) { 4 | return false; 5 | } 6 | 7 | return value.length <= 3; 8 | } 9 | 10 | export const isStringValid = (value: string): boolean => { 11 | for (let i = 0; i <= 8; i += 1) { 12 | if ((i === 0 || i === 3 || i === 6) && !['r', '-', ''].includes(value.charAt(i)) || 13 | (i === 1 || i === 4 || i === 7) && !['w', '-', ''].includes(value.charAt(i)) || 14 | (i === 2 || i === 5 || i === 8) && !['x', '-', ''].includes(value.charAt(i))) { 15 | return false; 16 | } 17 | } 18 | 19 | return value.length <= 9; 20 | } 21 | -------------------------------------------------------------------------------- /src/components/ChmodCalculator/Checkboxes/CheckboxGroup.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Checkbox from "./Checkbox"; 3 | import {CheckboxGroupType} from "../../../types/chmodCalculator"; 4 | 5 | const CheckboxGroup: React.FC = ({ownership, checked, handleOnChange}) => 6 |
7 | 8 |

{ownership}

9 | 10 | {['Read', 'Write', 'Execute'].map(permission => 11 | )} 16 | 17 |
18 | 19 | export default CheckboxGroup; 20 | -------------------------------------------------------------------------------- /src/tests/containers/CrontabGenerator/GenerateCrontabLine.test.tsx: -------------------------------------------------------------------------------- 1 | import {fireEvent, render, screen} from "@testing-library/react"; 2 | import {Provider} from "react-redux"; 3 | import React from "react"; 4 | import store from "../../../store/configureStore"; 5 | import GenerateCrontabLine 6 | from "../../../containers/CrontabGenerator/GenerateCrontabLine"; 7 | 8 | const setup = () => { 9 | render() 10 | } 11 | 12 | it('should simulate button click', () => { 13 | setup(); 14 | const button = screen.getByText('Generate Crontab Line'); 15 | expect(button).toBeVisible(); 16 | fireEvent.click(button); 17 | expect(screen.queryByText('Please enter a command.')).toBeVisible(); 18 | }); 19 | -------------------------------------------------------------------------------- /src/actions/PasswordGenerator/passwordGenerator.ts: -------------------------------------------------------------------------------- 1 | import { 2 | SET_PASSWORD, 3 | SET_PASSWORD_LENGTH, 4 | SET_CHECKED_PARAM, 5 | SET_COPY_TO_CLIPBOARD_STATE, 6 | } from "../../constants/ActionTypes"; 7 | import {CheckboxesType} from "../../types/passwordGenerator"; 8 | 9 | export const setPassword = (payload: string) => ({ 10 | type: SET_PASSWORD, payload 11 | }); 12 | 13 | export const setPasswordLength = (payload: string) => ({ 14 | type: SET_PASSWORD_LENGTH, payload 15 | }); 16 | 17 | export const setCheckedParam = (payload: CheckboxesType) => ({ 18 | type: SET_CHECKED_PARAM, payload 19 | }); 20 | 21 | export const setCopyToClipboardState = (payload: 'default' | 'pressed') => ({ 22 | type: SET_COPY_TO_CLIPBOARD_STATE, payload 23 | }); 24 | -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import './index.css'; 3 | import {Provider} from "react-redux"; 4 | import {createRoot} from "react-dom/client"; 5 | import App from './App'; 6 | import * as serviceWorker from './serviceWorker'; 7 | import store from "./store/configureStore"; 8 | 9 | const container = document.getElementById('root'); 10 | const root = createRoot(container!); 11 | root.render( 12 | 13 | 14 | 15 | ); 16 | 17 | // If you want your app to work offline and load faster, you can change 18 | // unregister() to register() below. Note this comes with some pitfalls. 19 | // Learn more about service workers: https://bit.ly/CRA-PWA 20 | serviceWorker.unregister(); 21 | -------------------------------------------------------------------------------- /src/tests/components/PasswordGenerator/FormFields/PasswordLength.test.tsx: -------------------------------------------------------------------------------- 1 | import {render, screen} from "@testing-library/react"; 2 | import React from "react"; 3 | import PasswordLength 4 | from "../../../../components/PasswordGenerator/FormFields/PasswordLength"; 5 | 6 | const onChange = jest.fn(); 7 | 8 | it('shows the slider title', () => { 9 | render(); 11 | expect(screen.getByText(/Password Length:/i)).toBeVisible(); 12 | }); 13 | 14 | it('shows the password length', () => { 15 | render(); 17 | expect(screen.getByText(/41/i)).toBeVisible(); 18 | }); 19 | -------------------------------------------------------------------------------- /src/tests/components/EpochConverter/CurrentEpochTime.test.tsx: -------------------------------------------------------------------------------- 1 | import {render, screen} from "@testing-library/react"; 2 | import React from "react"; 3 | import CurrentEpochTime from "../../../components/EpochConverter/CurrentEpochTime"; 4 | 5 | const copyToClipboard = jest.fn(); 6 | 7 | const setup = () => { 8 | render() 11 | } 12 | 13 | it('shows the current unix time', () => { 14 | setup(); 15 | expect(screen.getByText('111222333')).toBeVisible() 16 | }); 17 | 18 | it('shows the "Copy" button', () => { 19 | setup(); 20 | expect(screen.getByText('Copy')).toBeVisible() 21 | }); 22 | -------------------------------------------------------------------------------- /src/tests/containers/EpochConverter/UnixTimeForm.test.tsx: -------------------------------------------------------------------------------- 1 | import {fireEvent, render, screen} from "@testing-library/react"; 2 | import React from "react"; 3 | import {Provider} from "react-redux"; 4 | import UnixTimeForm from "../../../containers/EpochConverter/UnixTimeForm"; 5 | import store from "../../../store/configureStore"; 6 | 7 | test('Timestamp to Human date', () => { 8 | render(); 9 | expect(screen.getByText('Timestamp to Human date')).toBeInTheDocument(); 10 | expect(screen.getByDisplayValue((Math.floor(Date.now() / 1000)).toString())).toBeInTheDocument(); 11 | const btn = screen.getByText("Timestamp to Human date"); 12 | fireEvent.click(btn); 13 | expect(screen.getByText(/GMT/i)).toBeInTheDocument(); 14 | }); 15 | -------------------------------------------------------------------------------- /src/components/Menubar/Menubar.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import {Menubar as PrimeMenubar} from 'primereact/menubar'; 3 | import {Button} from "primereact/button"; 4 | import {MenubarPropsType} from "../../types/menubar"; 5 | 6 | const Menubar: React.FC = ({model, onLogoClick, onGitHubClick}) => { 7 | 8 | const start =