aboutsummaryrefslogtreecommitdiff
path: root/src/components/configuration
diff options
context:
space:
mode:
Diffstat (limited to 'src/components/configuration')
-rw-r--r--src/components/configuration/checkBoxWrapper.tsx37
-rw-r--r--src/components/configuration/config.tsx248
-rw-r--r--src/components/configuration/inputWrapper.tsx41
-rw-r--r--src/components/configuration/selectWrapper.tsx44
-rw-r--r--src/components/configuration/textAreaWrapper.tsx59
5 files changed, 429 insertions, 0 deletions
diff --git a/src/components/configuration/checkBoxWrapper.tsx b/src/components/configuration/checkBoxWrapper.tsx
new file mode 100644
index 0000000..e9dd1eb
--- /dev/null
+++ b/src/components/configuration/checkBoxWrapper.tsx
@@ -0,0 +1,37 @@
+import ConfigType from '../../../common/types/configType'
+
+type CheckBoxProps = {
+ title: string
+ keyName: keyof ConfigType
+ checked?: boolean
+ disabled?: boolean
+
+ handleChange: (value: any, key: keyof ConfigType) => void
+}
+
+const CheckBoxWrapper = ({
+ title,
+ keyName,
+ checked,
+ disabled,
+ handleChange
+}: CheckBoxProps) => {
+ return (
+ <div className="form-control">
+ <label className="label cursor-pointer justify-start gap-2">
+ <input
+ className="checkbox checkbox-sm"
+ type="checkbox"
+ checked={!!checked}
+ disabled={disabled}
+ onChange={(e) => {
+ handleChange({ state: e.target.checked }, keyName)
+ }}
+ />
+ <span className="label-text">{title}</span>
+ </label>
+ </div>
+ )
+}
+
+export default CheckBoxWrapper
diff --git a/src/components/configuration/config.tsx b/src/components/configuration/config.tsx
new file mode 100644
index 0000000..9331f87
--- /dev/null
+++ b/src/components/configuration/config.tsx
@@ -0,0 +1,248 @@
+import React, { useContext, useEffect } from 'react'
+import { useRouter } from 'next/router'
+
+import { RepoQueryResponse } from '../../../common/github/repoQuery'
+import ConfigContext from '../../contexts/ConfigContext'
+
+import ConfigType, {
+ Theme,
+ Pattern,
+ Font,
+ RequiredConfigsKeys
+} from '../../../common/types/configType'
+
+import { getOptionalConfig } from '../../../common/configHelper'
+
+import SelectWrapper from './selectWrapper'
+import CheckBoxWrapper from './checkBoxWrapper'
+import InputWrapper from './inputWrapper'
+import TextAreaWrapper from './textAreaWrapper'
+
+type ConfigProp = {
+ repository: RepoQueryResponse['repository']
+}
+
+const Config = ({ repository }: ConfigProp) => {
+ const router = useRouter()
+
+ const { config, setConfig } = useContext(ConfigContext)
+
+ const handleChanges = (changes: { value: any; key: keyof ConfigType }[]) => {
+ let newConfig: ConfigType = { ...config }
+ const urlParams = router.query
+ // Remove extraneous params from route
+ delete urlParams._owner
+ delete urlParams._name
+ changes.forEach(({ value, key }) => {
+ const currentValue = newConfig[key] ? newConfig[key] : {}
+ if (value.required === true) {
+ newConfig = { ...newConfig, [key]: value.val }
+ } else {
+ newConfig = { ...newConfig, [key]: { ...currentValue, ...value } }
+ }
+
+ if (value && value.state === true && value.editable) {
+ urlParams[key] = '1'
+ urlParams[`${key}Editable`] = value.value
+ } else if (value && value.state === true) {
+ urlParams[key] = '1'
+ } else if (value && value.required === true) {
+ urlParams[key] = value.val
+ } else {
+ urlParams[key] = '0'
+ }
+
+ if (!urlParams[key] || urlParams[key] === '0') {
+ delete urlParams[key]
+ if (`${key}Editable` in urlParams) {
+ delete urlParams[`${key}Editable`]
+ }
+ }
+ })
+
+ router.push(
+ `${window.location.pathname}?${Object.entries(urlParams)
+ .sort()
+ .map(([k, v]) => `${k}=${encodeURIComponent(String(v))}`)
+ .join('&')}`,
+ undefined,
+ { shallow: true }
+ )
+ }
+
+ const handleChange = (value: any, key: keyof ConfigType) => {
+ handleChanges([{ value, key }])
+ }
+
+ useEffect(() => {
+ const handleRouteChange = (asPath: string) => {
+ if (repository) {
+ const newConfig = getOptionalConfig(repository)
+ if (newConfig) {
+ const params = new URLSearchParams(asPath.split('?')[1])
+
+ Array.from(params.keys()).forEach((stringKey) => {
+ const key = stringKey as keyof ConfigType
+ if (key in newConfig) {
+ const query = params.get(key)
+ const currentConfig = newConfig[key as keyof typeof newConfig]
+ const newChange = {
+ state: query === '1'
+ }
+ if (currentConfig?.editable) {
+ const editableValue = params.get(`${key}Editable`)
+ if (editableValue != null) {
+ Object.assign(newChange, {
+ value: editableValue
+ })
+ }
+ }
+
+ Object.assign(
+ newConfig[key as keyof typeof newConfig] ?? {},
+ newChange
+ )
+ } else if (key in RequiredConfigsKeys) {
+ const query = params.get(key)
+ if (query != null) {
+ const newChange = {
+ [key]: query
+ }
+
+ Object.assign(newConfig, newChange)
+ }
+ }
+ })
+ setConfig({ ...config, ...newConfig })
+ }
+ }
+ }
+
+ router.events.on('routeChangeComplete', handleRouteChange)
+ handleRouteChange(router.asPath)
+
+ return () => {
+ router.events.off('routeChangeComplete', handleRouteChange)
+ }
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [])
+
+ if (!repository) {
+ return null
+ }
+
+ return (
+ <div className="card w-96 max-w-[90vw] bg-base-200 text-primary-content shadow-xl">
+ <div className="card-body">
+ <SelectWrapper
+ title="Theme"
+ keyName="theme"
+ map={Object.keys(Theme).map((key) => ({
+ key,
+ label: (Theme as any)[key]
+ }))}
+ value={config.theme}
+ handleChange={handleChange}
+ />
+ <SelectWrapper
+ title="Font"
+ keyName="font"
+ map={Object.keys(Font).map((key) => ({
+ key,
+ label: (Font as any)[key]
+ }))}
+ value={config.font}
+ handleChange={handleChange}
+ />
+ <SelectWrapper
+ title="Background Pattern"
+ keyName="pattern"
+ map={Object.keys(Pattern).map((key) => ({
+ key,
+ label: (Pattern as any)[key]
+ }))}
+ value={config.pattern}
+ handleChange={handleChange}
+ />
+ <InputWrapper
+ title="Logo"
+ alt="Image url or data uri"
+ keyName="logo"
+ placeholder="Optional"
+ value={config.logo}
+ handleChange={handleChange}
+ />
+
+ <div className="columns-2">
+ <CheckBoxWrapper
+ title="Name"
+ keyName="name"
+ checked={config.name?.state}
+ handleChange={handleChange}
+ disabled
+ />
+ <CheckBoxWrapper
+ title="Owner"
+ keyName="owner"
+ checked={config.owner?.state}
+ handleChange={handleChange}
+ />
+ <CheckBoxWrapper
+ title="Language"
+ keyName="language"
+ checked={config.language?.state}
+ handleChange={handleChange}
+ />
+ <CheckBoxWrapper
+ title="2nd Language"
+ keyName="language2"
+ checked={config.language2?.state}
+ handleChange={handleChange}
+ />
+ <CheckBoxWrapper
+ title="Stars"
+ keyName="stargazers"
+ checked={config.stargazers?.state}
+ handleChange={handleChange}
+ />
+ <CheckBoxWrapper
+ title="Forks"
+ keyName="forks"
+ checked={config.forks?.state}
+ handleChange={handleChange}
+ />
+ <CheckBoxWrapper
+ title="Issues"
+ keyName="issues"
+ checked={config.issues?.state}
+ handleChange={handleChange}
+ />
+ <CheckBoxWrapper
+ title="Pull Requests"
+ keyName="pulls"
+ checked={config.pulls?.state}
+ handleChange={handleChange}
+ />
+ <CheckBoxWrapper
+ title="Description"
+ keyName="description"
+ checked={config.description?.state}
+ handleChange={handleChange}
+ />
+ </div>
+
+ {config.description?.state && (
+ <TextAreaWrapper
+ title="Description"
+ keyName="description"
+ value={config.description?.value}
+ handleChange={handleChange}
+ disabled={!config.description?.state}
+ />
+ )}
+ </div>
+ </div>
+ )
+}
+
+export default Config
diff --git a/src/components/configuration/inputWrapper.tsx b/src/components/configuration/inputWrapper.tsx
new file mode 100644
index 0000000..0d3fd95
--- /dev/null
+++ b/src/components/configuration/inputWrapper.tsx
@@ -0,0 +1,41 @@
+import ConfigType from '../../../common/types/configType'
+
+type InputProps = {
+ title: string
+ alt?: string
+ keyName: keyof ConfigType
+ value: string
+ placeholder: string
+ disabled?: boolean
+ handleChange: (value: any, key: keyof ConfigType) => void
+}
+
+const InputWrapper = ({
+ title,
+ alt,
+ keyName,
+ value,
+ placeholder,
+ disabled,
+ handleChange
+}: InputProps) => {
+ return (
+ <div className="form-control w-full" style={{ display: 'none' }}>
+ <label className="label">
+ <span className="label-text">{title}</span>
+ {alt && <span className="label-text-alt">{alt}</span>}
+ </label>
+ <input
+ className="input input-bordered w-full input-sm"
+ type="text"
+ value={value || ''}
+ disabled={!!disabled}
+ placeholder={placeholder}
+ onChange={(e) => {
+ handleChange({ val: e.target.value, required: true }, keyName)
+ }}
+ />
+ </div>
+ )
+}
+export default InputWrapper
diff --git a/src/components/configuration/selectWrapper.tsx b/src/components/configuration/selectWrapper.tsx
new file mode 100644
index 0000000..81c3709
--- /dev/null
+++ b/src/components/configuration/selectWrapper.tsx
@@ -0,0 +1,44 @@
+import ConfigType from '../../../common/types/configType'
+
+type SelectWrapperProps = {
+ title: string
+ alt?: string
+ keyName: keyof ConfigType
+ map: { key: string; label: any }[]
+ value: string
+ handleChange: (value: any, key: keyof ConfigType) => void
+}
+
+const SelectWrapper = ({
+ title,
+ alt,
+ keyName,
+ map,
+ value,
+ handleChange
+}: SelectWrapperProps) => {
+ return (
+ <div className="form-control w-full">
+ <label className="label">
+ <span className="label-text">{title}</span>
+ {alt && <span className="label-text-alt">{alt}</span>}
+ </label>
+ <select
+ className="select select-bordered select-sm"
+ onChange={(e) => {
+ handleChange({ val: e.target.value, required: true }, keyName)
+ }}
+ value={value}>
+ {map.map(({ key, label }) => {
+ return (
+ <option key={key} value={label}>
+ {label}
+ </option>
+ )
+ })}
+ </select>
+ </div>
+ )
+}
+
+export default SelectWrapper
diff --git a/src/components/configuration/textAreaWrapper.tsx b/src/components/configuration/textAreaWrapper.tsx
new file mode 100644
index 0000000..6a30f42
--- /dev/null
+++ b/src/components/configuration/textAreaWrapper.tsx
@@ -0,0 +1,59 @@
+import React, { useEffect, useState } from 'react'
+
+import { useDebouncedCallback } from 'use-debounce'
+
+import ConfigType from '../../../common/types/configType'
+
+type TextAreaProps = {
+ title?: string
+ alt?: string
+ value: string
+ placeholder?: string
+ keyName: keyof ConfigType
+ handleChange: (value: any, key: keyof ConfigType) => void
+ disabled?: boolean
+}
+
+const TextAreaWrapper = ({
+ title,
+ alt,
+ keyName,
+ value,
+ placeholder,
+ handleChange,
+ disabled
+}: TextAreaProps) => {
+ const [internalValue, setInternalValue] = useState(value)
+
+ const debounced = useDebouncedCallback((value) => {
+ handleChange({ value, editable: true, state: true }, keyName)
+ }, 500)
+
+ const processChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
+ setInternalValue(e.target.value)
+ debounced(e.target.value)
+ }
+
+ useEffect(() => {
+ setInternalValue(value)
+ }, [value])
+
+ return (
+ <div className="form-control" style={{ display: 'none' }}>
+ {title && (
+ <label className="label">
+ <span className="label-text">{title}</span>
+ {alt && <span className="label-text-alt">{alt}</span>}
+ </label>
+ )}
+ <textarea
+ className="textarea textarea-bordered h-20"
+ value={internalValue}
+ onChange={processChange}
+ disabled={disabled}
+ placeholder={placeholder}
+ />
+ </div>
+ )
+}
+export default TextAreaWrapper