First UI Pass (#15)
* Added scratchpad dir * Installed Alpine * [WIP] began integrating Tailwind and accompanying theme * [WIP] Set up basic views for sources * Adds new UI to media profiles page * Removes unneeded view
This commit is contained in:
@@ -40,3 +40,4 @@ npm-debug.log
|
||||
.env
|
||||
.DS_Store
|
||||
scratchpad.md
|
||||
/scratchpad/
|
||||
|
||||
@@ -2,4 +2,30 @@
|
||||
@import 'tailwindcss/components';
|
||||
@import 'tailwindcss/utilities';
|
||||
|
||||
@import './satoshi';
|
||||
|
||||
/* This file is for your main application CSS */
|
||||
[x-cloak] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
@layer base {
|
||||
body {
|
||||
@apply relative z-1 bg-whiten font-satoshi text-base font-normal text-body;
|
||||
}
|
||||
}
|
||||
|
||||
@layer components {
|
||||
}
|
||||
|
||||
@layer utilities {
|
||||
/* Chrome, Safari and Opera */
|
||||
.no-scrollbar::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.no-scrollbar {
|
||||
-ms-overflow-style: none; /* IE and Edge */
|
||||
scrollbar-width: none; /* Firefox */
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
/**
|
||||
* @license
|
||||
*
|
||||
* Font Family: Satoshi
|
||||
* Designed by: Deni Anggara
|
||||
* URL: https://www.fontshare.com/fonts/satoshi
|
||||
* © 2023 Indian Type Foundry
|
||||
*
|
||||
* Font Styles:
|
||||
* Satoshi Light
|
||||
* Satoshi Light Italic
|
||||
* Satoshi Regular
|
||||
* Satoshi Italic
|
||||
* Satoshi Medium
|
||||
* Satoshi Medium Italic
|
||||
* Satoshi Bold
|
||||
* Satoshi Bold Italic
|
||||
* Satoshi Black
|
||||
* Satoshi Black Italic
|
||||
*
|
||||
*/
|
||||
|
||||
@font-face {
|
||||
font-family: 'Satoshi';
|
||||
src: url('/fonts/satoshi/Satoshi-Light.woff2'), url('/fonts/satoshi/Satoshi-Light.woff'),
|
||||
url('/fonts/satoshi/Satoshi-Light.ttf');
|
||||
font-weight: 300;
|
||||
font-display: swap;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Satoshi';
|
||||
src: url('/fonts/satoshi/Satoshi-LightItalic.woff2'),
|
||||
url('/fonts/satoshi/Satoshi-LightItalic.woff'), url('/fonts/satoshi/Satoshi-LightItalic.ttf');
|
||||
font-weight: 300;
|
||||
font-display: swap;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Satoshi';
|
||||
src: url('/fonts/satoshi/Satoshi-Regular.woff2'), url('/fonts/satoshi/Satoshi-Regular.woff'),
|
||||
url('/fonts/satoshi/Satoshi-Regular.ttf');
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Satoshi';
|
||||
src: url('/fonts/satoshi/Satoshi-Italic.woff2'), url('/fonts/satoshi/Satoshi-Italic.woff'),
|
||||
url('/fonts/satoshi/Satoshi-Italic.ttf');
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Satoshi';
|
||||
src: url('/fonts/satoshi/Satoshi-Medium.woff2'), url('/fonts/satoshi/Satoshi-Medium.woff'),
|
||||
url('/fonts/satoshi/Satoshi-Medium.ttf');
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Satoshi';
|
||||
src: url('/fonts/satoshi/Satoshi-MediumItalic.woff2'),
|
||||
url('/fonts/satoshi/Satoshi-MediumItalic.woff'), url('/fonts/satoshi/Satoshi-MediumItalic.ttf');
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Satoshi';
|
||||
src: url('/fonts/satoshi/Satoshi-Bold.woff2'), url('/fonts/satoshi/Satoshi-Bold.woff'),
|
||||
url('/fonts/satoshi/Satoshi-Bold.ttf');
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Satoshi';
|
||||
src: url('/fonts/satoshi/Satoshi-BoldItalic.woff2'), url('/fonts/satoshi/Satoshi-BoldItalic.woff'),
|
||||
url('/fonts/satoshi/Satoshi-BoldItalic.ttf');
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Satoshi';
|
||||
src: url('/fonts/satoshi/Satoshi-BlackItalic.woff2'),
|
||||
url('/fonts/satoshi/Satoshi-BlackItalic.woff'), url('/fonts/satoshi/Satoshi-BlackItalic.ttf');
|
||||
font-weight: 900;
|
||||
font-display: swap;
|
||||
font-style: italic;
|
||||
}
|
||||
+12
-1
@@ -21,10 +21,21 @@ import 'phoenix_html'
|
||||
import { Socket } from 'phoenix'
|
||||
import { LiveSocket } from 'phoenix_live_view'
|
||||
import topbar from '../vendor/topbar'
|
||||
import Alpine from 'alpinejs'
|
||||
|
||||
window.Alpine = Alpine
|
||||
Alpine.start()
|
||||
|
||||
let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute('content')
|
||||
let liveSocket = new LiveSocket('/live', Socket, {
|
||||
params: { _csrf_token: csrfToken }
|
||||
params: { _csrf_token: csrfToken },
|
||||
dom: {
|
||||
onBeforeElUpdated(from, to) {
|
||||
if (from._x_dataStack) {
|
||||
window.Alpine.clone(from, to)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// Show progress bar on live navigation and form submits
|
||||
|
||||
+4
-1
@@ -1,3 +1,6 @@
|
||||
{
|
||||
"description": "Use this one for actual JS deps used in-app"
|
||||
"description": "Use this one for actual JS deps used in-app",
|
||||
"dependencies": {
|
||||
"alpinejs": "^3.13.5"
|
||||
}
|
||||
}
|
||||
|
||||
+274
-1
@@ -2,15 +2,288 @@
|
||||
// https://tailwindcss.com/docs/configuration
|
||||
|
||||
const plugin = require('tailwindcss/plugin')
|
||||
const defaultTheme = require('tailwindcss/defaultTheme')
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
|
||||
module.exports = {
|
||||
content: ['./js/**/*.js', '../lib/pinchflat_web.ex', '../lib/pinchflat_web/**/*.*ex'],
|
||||
darkMode: 'class',
|
||||
theme: {
|
||||
fontFamily: {
|
||||
satoshi: ['Satoshi', 'sans-serif']
|
||||
},
|
||||
screens: {
|
||||
'2xsm': '375px',
|
||||
xsm: '425px',
|
||||
'3xl': '2000px',
|
||||
...defaultTheme.screens
|
||||
},
|
||||
extend: {
|
||||
colors: {
|
||||
brand: '#FD4F00'
|
||||
current: 'currentColor',
|
||||
transparent: 'transparent',
|
||||
white: '#FFFFFF',
|
||||
black: '#1C2434',
|
||||
'black-2': '#010101',
|
||||
body: '#64748B',
|
||||
bodydark: '#AEB7C0',
|
||||
bodydark1: '#DEE4EE',
|
||||
bodydark2: '#8A99AF',
|
||||
primary: '#3C50E0',
|
||||
secondary: '#80CAEE',
|
||||
stroke: '#E2E8F0',
|
||||
gray: '#EFF4FB',
|
||||
graydark: '#333A48',
|
||||
'gray-2': '#F7F9FC',
|
||||
'gray-3': '#FAFAFA',
|
||||
whiten: '#F1F5F9',
|
||||
whiter: '#F5F7FD',
|
||||
boxdark: '#24303F',
|
||||
'boxdark-2': '#1A222C',
|
||||
strokedark: '#2E3A47',
|
||||
'form-strokedark': '#3d4d60',
|
||||
'form-input': '#1d2a39',
|
||||
'meta-1': '#DC3545',
|
||||
'meta-2': '#EFF2F7',
|
||||
'meta-3': '#10B981',
|
||||
'meta-4': '#313D4A',
|
||||
'meta-5': '#259AE6',
|
||||
'meta-6': '#FFBA00',
|
||||
'meta-7': '#FF6766',
|
||||
'meta-8': '#F0950C',
|
||||
'meta-9': '#E5E7EB',
|
||||
success: '#219653',
|
||||
danger: '#D34053',
|
||||
warning: '#FFA70B'
|
||||
},
|
||||
fontSize: {
|
||||
'title-xxl': ['44px', '55px'],
|
||||
'title-xl': ['36px', '45px'],
|
||||
'title-xl2': ['33px', '45px'],
|
||||
'title-lg': ['28px', '35px'],
|
||||
'title-md': ['24px', '30px'],
|
||||
'title-md2': ['26px', '30px'],
|
||||
'title-sm': ['20px', '26px'],
|
||||
'title-xsm': ['18px', '24px']
|
||||
},
|
||||
spacing: {
|
||||
4.5: '1.125rem',
|
||||
5.5: '1.375rem',
|
||||
6.5: '1.625rem',
|
||||
7.5: '1.875rem',
|
||||
8.5: '2.125rem',
|
||||
9.5: '2.375rem',
|
||||
10.5: '2.625rem',
|
||||
11: '2.75rem',
|
||||
11.5: '2.875rem',
|
||||
12.5: '3.125rem',
|
||||
13: '3.25rem',
|
||||
13.5: '3.375rem',
|
||||
14: '3.5rem',
|
||||
14.5: '3.625rem',
|
||||
15: '3.75rem',
|
||||
15.5: '3.875rem',
|
||||
16: '4rem',
|
||||
16.5: '4.125rem',
|
||||
17: '4.25rem',
|
||||
17.5: '4.375rem',
|
||||
18: '4.5rem',
|
||||
18.5: '4.625rem',
|
||||
19: '4.75rem',
|
||||
19.5: '4.875rem',
|
||||
21: '5.25rem',
|
||||
21.5: '5.375rem',
|
||||
22: '5.5rem',
|
||||
22.5: '5.625rem',
|
||||
24.5: '6.125rem',
|
||||
25: '6.25rem',
|
||||
25.5: '6.375rem',
|
||||
26: '6.5rem',
|
||||
27: '6.75rem',
|
||||
27.5: '6.875rem',
|
||||
29: '7.25rem',
|
||||
29.5: '7.375rem',
|
||||
30: '7.5rem',
|
||||
31: '7.75rem',
|
||||
32.5: '8.125rem',
|
||||
34: '8.5rem',
|
||||
34.5: '8.625rem',
|
||||
35: '8.75rem',
|
||||
36.5: '9.125rem',
|
||||
37.5: '9.375rem',
|
||||
39: '9.75rem',
|
||||
39.5: '9.875rem',
|
||||
40: '10rem',
|
||||
42.5: '10.625rem',
|
||||
44: '11rem',
|
||||
45: '11.25rem',
|
||||
46: '11.5rem',
|
||||
47.5: '11.875rem',
|
||||
49: '12.25rem',
|
||||
50: '12.5rem',
|
||||
52: '13rem',
|
||||
52.5: '13.125rem',
|
||||
54: '13.5rem',
|
||||
54.5: '13.625rem',
|
||||
55: '13.75rem',
|
||||
55.5: '13.875rem',
|
||||
59: '14.75rem',
|
||||
60: '15rem',
|
||||
62.5: '15.625rem',
|
||||
65: '16.25rem',
|
||||
67: '16.75rem',
|
||||
67.5: '16.875rem',
|
||||
70: '17.5rem',
|
||||
72.5: '18.125rem',
|
||||
73: '18.25rem',
|
||||
75: '18.75rem',
|
||||
90: '22.5rem',
|
||||
94: '23.5rem',
|
||||
95: '23.75rem',
|
||||
100: '25rem',
|
||||
115: '28.75rem',
|
||||
125: '31.25rem',
|
||||
132.5: '33.125rem',
|
||||
150: '37.5rem',
|
||||
171.5: '42.875rem',
|
||||
180: '45rem',
|
||||
187.5: '46.875rem',
|
||||
203: '50.75rem',
|
||||
230: '57.5rem',
|
||||
242.5: '60.625rem'
|
||||
},
|
||||
maxWidth: {
|
||||
2.5: '0.625rem',
|
||||
3: '0.75rem',
|
||||
4: '1rem',
|
||||
11: '2.75rem',
|
||||
13: '3.25rem',
|
||||
14: '3.5rem',
|
||||
15: '3.75rem',
|
||||
22.5: '5.625rem',
|
||||
25: '6.25rem',
|
||||
30: '7.5rem',
|
||||
34: '8.5rem',
|
||||
35: '8.75rem',
|
||||
40: '10rem',
|
||||
42.5: '10.625rem',
|
||||
44: '11rem',
|
||||
45: '11.25rem',
|
||||
60: '15rem',
|
||||
70: '17.5rem',
|
||||
90: '22.5rem',
|
||||
94: '23.5rem',
|
||||
125: '31.25rem',
|
||||
132.5: '33.125rem',
|
||||
142.5: '35.625rem',
|
||||
150: '37.5rem',
|
||||
180: '45rem',
|
||||
203: '50.75rem',
|
||||
230: '57.5rem',
|
||||
242.5: '60.625rem',
|
||||
270: '67.5rem',
|
||||
280: '70rem',
|
||||
292.5: '73.125rem'
|
||||
},
|
||||
maxHeight: {
|
||||
35: '8.75rem',
|
||||
70: '17.5rem',
|
||||
90: '22.5rem',
|
||||
550: '34.375rem',
|
||||
300: '18.75rem'
|
||||
},
|
||||
minWidth: {
|
||||
22.5: '5.625rem',
|
||||
42.5: '10.625rem',
|
||||
47.5: '11.875rem',
|
||||
75: '18.75rem'
|
||||
},
|
||||
zIndex: {
|
||||
999999: '999999',
|
||||
99999: '99999',
|
||||
9999: '9999',
|
||||
999: '999',
|
||||
99: '99',
|
||||
9: '9',
|
||||
1: '1'
|
||||
},
|
||||
opacity: {
|
||||
65: '.65'
|
||||
},
|
||||
transitionProperty: { width: 'width', stroke: 'stroke' },
|
||||
borderWidth: {
|
||||
6: '6px'
|
||||
},
|
||||
boxShadow: {
|
||||
default: '0px 8px 13px -3px rgba(0, 0, 0, 0.07)',
|
||||
card: '0px 1px 3px rgba(0, 0, 0, 0.12)',
|
||||
'card-2': '0px 1px 2px rgba(0, 0, 0, 0.05)',
|
||||
switcher:
|
||||
'0px 2px 4px rgba(0, 0, 0, 0.2), inset 0px 2px 2px #FFFFFF, inset 0px -1px 1px rgba(0, 0, 0, 0.1)',
|
||||
'switch-1': '0px 0px 5px rgba(0, 0, 0, 0.15)',
|
||||
1: '0px 1px 3px rgba(0, 0, 0, 0.08)',
|
||||
2: '0px 1px 4px rgba(0, 0, 0, 0.12)',
|
||||
3: '0px 1px 5px rgba(0, 0, 0, 0.14)',
|
||||
4: '0px 4px 10px rgba(0, 0, 0, 0.12)',
|
||||
5: '0px 1px 1px rgba(0, 0, 0, 0.15)',
|
||||
6: '0px 3px 15px rgba(0, 0, 0, 0.1)',
|
||||
7: '-5px 0 0 #313D4A, 5px 0 0 #313D4A',
|
||||
8: '1px 0 0 #313D4A, -1px 0 0 #313D4A, 0 1px 0 #313D4A, 0 -1px 0 #313D4A, 0 3px 13px rgb(0 0 0 / 8%)'
|
||||
},
|
||||
dropShadow: {
|
||||
1: '0px 1px 0px #E2E8F0',
|
||||
2: '0px 1px 4px rgba(0, 0, 0, 0.12)'
|
||||
},
|
||||
keyframes: {
|
||||
linspin: {
|
||||
'100%': { transform: 'rotate(360deg)' }
|
||||
},
|
||||
easespin: {
|
||||
'12.5%': { transform: 'rotate(135deg)' },
|
||||
'25%': { transform: 'rotate(270deg)' },
|
||||
'37.5%': { transform: 'rotate(405deg)' },
|
||||
'50%': { transform: 'rotate(540deg)' },
|
||||
'62.5%': { transform: 'rotate(675deg)' },
|
||||
'75%': { transform: 'rotate(810deg)' },
|
||||
'87.5%': { transform: 'rotate(945deg)' },
|
||||
'100%': { transform: 'rotate(1080deg)' }
|
||||
},
|
||||
'left-spin': {
|
||||
'0%': { transform: 'rotate(130deg)' },
|
||||
'50%': { transform: 'rotate(-5deg)' },
|
||||
'100%': { transform: 'rotate(130deg)' }
|
||||
},
|
||||
'right-spin': {
|
||||
'0%': { transform: 'rotate(-130deg)' },
|
||||
'50%': { transform: 'rotate(5deg)' },
|
||||
'100%': { transform: 'rotate(-130deg)' }
|
||||
},
|
||||
rotating: {
|
||||
'0%, 100%': { transform: 'rotate(360deg)' },
|
||||
'50%': { transform: 'rotate(0deg)' }
|
||||
},
|
||||
topbottom: {
|
||||
'0%, 100%': { transform: 'translate3d(0, -100%, 0)' },
|
||||
'50%': { transform: 'translate3d(0, 0, 0)' }
|
||||
},
|
||||
bottomtop: {
|
||||
'0%, 100%': { transform: 'translate3d(0, 0, 0)' },
|
||||
'50%': { transform: 'translate3d(0, -100%, 0)' }
|
||||
}
|
||||
},
|
||||
animation: {
|
||||
linspin: 'linspin 1568.2353ms linear infinite',
|
||||
easespin: 'easespin 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both',
|
||||
'left-spin': 'left-spin 1333ms cubic-bezier(0.4, 0, 0.2, 1) infinite both',
|
||||
'right-spin': 'right-spin 1333ms cubic-bezier(0.4, 0, 0.2, 1) infinite both',
|
||||
'ping-once': 'ping 5s cubic-bezier(0, 0, 0.2, 1)',
|
||||
rotating: 'rotating 30s linear infinite',
|
||||
topbottom: 'topbottom 60s infinite alternate linear',
|
||||
bottomtop: 'bottomtop 60s infinite alternate linear',
|
||||
'spin-1.5': 'spin 1.5s linear infinite',
|
||||
'spin-2': 'spin 2s linear infinite',
|
||||
'spin-3': 'spin 3s linear infinite'
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -2,3 +2,21 @@
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@vue/reactivity@~3.1.1":
|
||||
version "3.1.5"
|
||||
resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.1.5.tgz#dbec4d9557f7c8f25c2635db1e23a78a729eb991"
|
||||
integrity sha512-1tdfLmNjWG6t/CsPldh+foumYFo3cpyCHgBYQ34ylaMsJ+SNHQ1kApMIa8jN+i593zQuaw3AdWH0nJTARzCFhg==
|
||||
dependencies:
|
||||
"@vue/shared" "3.1.5"
|
||||
|
||||
"@vue/shared@3.1.5":
|
||||
version "3.1.5"
|
||||
resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.1.5.tgz#74ee3aad995d0a3996a6bb9533d4d280514ede03"
|
||||
integrity sha512-oJ4F3TnvpXaQwZJNF3ZK+kLPHKarDmJjJ6jyzVNDKH9md1dptjC7lWR//jrGuLdek/U6iltWxqAnYOu8gCiOvA==
|
||||
|
||||
alpinejs@^3.13.5:
|
||||
version "3.13.5"
|
||||
resolved "https://registry.yarnpkg.com/alpinejs/-/alpinejs-3.13.5.tgz#c06f067e847a295085b4c6f76c42a6a35499d953"
|
||||
integrity sha512-1d2XeNGN+Zn7j4mUAKXtAgdc4/rLeadyTMWeJGXF5DzwawPBxwTiBhFFm6w/Ei8eJxUZeyNWWSD9zknfdz1kEw==
|
||||
dependencies:
|
||||
"@vue/reactivity" "~3.1.1"
|
||||
|
||||
@@ -27,7 +27,8 @@ defmodule Pinchflat.Profiles.MediaProfile do
|
||||
|
||||
schema "media_profiles" do
|
||||
field :name, :string
|
||||
field :output_path_template, :string
|
||||
|
||||
field :output_path_template, :string, default: "/{{ uploader }}/{{ title }}.{{ ext }}"
|
||||
|
||||
field :download_subs, :boolean, default: true
|
||||
field :download_auto_subs, :boolean, default: true
|
||||
|
||||
@@ -84,8 +84,10 @@ defmodule PinchflatWeb do
|
||||
# HTML escaping functionality
|
||||
import Phoenix.HTML
|
||||
# Core UI components and translation
|
||||
import PinchflatWeb.CoreComponents
|
||||
import PinchflatWeb.Gettext
|
||||
import PinchflatWeb.CoreComponents
|
||||
import PinchflatWeb.CustomComponents.TableComponents
|
||||
import PinchflatWeb.CustomComponents.ButtonComponents
|
||||
|
||||
# Shortcut for generating JS commands
|
||||
alias Phoenix.LiveView.JS
|
||||
|
||||
@@ -201,46 +201,14 @@ defmodule PinchflatWeb.CoreComponents do
|
||||
def simple_form(assigns) do
|
||||
~H"""
|
||||
<.form :let={f} for={@for} as={@as} {@rest}>
|
||||
<div class="mt-10 space-y-8 bg-white">
|
||||
<%= render_slot(@inner_block, f) %>
|
||||
<div :for={action <- @actions} class="mt-2 flex items-center justify-between gap-6">
|
||||
<%= render_slot(action, f) %>
|
||||
</div>
|
||||
<%= render_slot(@inner_block, f) %>
|
||||
<div :for={action <- @actions} class="mt-2 flex items-center justify-between gap-6">
|
||||
<%= render_slot(action, f) %>
|
||||
</div>
|
||||
</.form>
|
||||
"""
|
||||
end
|
||||
|
||||
@doc """
|
||||
Renders a button.
|
||||
|
||||
## Examples
|
||||
|
||||
<.button>Send!</.button>
|
||||
<.button phx-click="go" class="ml-2">Send!</.button>
|
||||
"""
|
||||
attr :type, :string, default: nil
|
||||
attr :class, :string, default: nil
|
||||
attr :rest, :global, include: ~w(disabled form name value)
|
||||
|
||||
slot :inner_block, required: true
|
||||
|
||||
def button(assigns) do
|
||||
~H"""
|
||||
<button
|
||||
type={@type}
|
||||
class={[
|
||||
"phx-submit-loading:opacity-75 rounded-lg bg-zinc-900 hover:bg-zinc-700 py-2 px-3",
|
||||
"text-sm font-semibold leading-6 text-white active:text-white/80",
|
||||
@class
|
||||
]}
|
||||
{@rest}
|
||||
>
|
||||
<%= render_slot(@inner_block) %>
|
||||
</button>
|
||||
"""
|
||||
end
|
||||
|
||||
@doc """
|
||||
Renders an input with label and error messages.
|
||||
|
||||
@@ -270,11 +238,12 @@ defmodule PinchflatWeb.CoreComponents do
|
||||
attr :name, :any
|
||||
attr :label, :string, default: nil
|
||||
attr :value, :any
|
||||
attr :help, :string, default: nil
|
||||
|
||||
attr :type, :string,
|
||||
default: "text",
|
||||
values: ~w(checkbox color date datetime-local email file hidden month number password
|
||||
range radio search select tel text textarea time url week)
|
||||
toggle range radio search select tel text textarea time url week)
|
||||
|
||||
attr :field, Phoenix.HTML.FormField,
|
||||
doc: "a form field struct retrieved from the form, for example: @form[:email]"
|
||||
@@ -308,7 +277,7 @@ defmodule PinchflatWeb.CoreComponents do
|
||||
|
||||
~H"""
|
||||
<div phx-feedback-for={@name}>
|
||||
<label class="flex items-center gap-4 text-sm leading-6 text-zinc-600">
|
||||
<label class="flex items-center gap-4 text-sm leading-6">
|
||||
<input type="hidden" name={@name} value="false" />
|
||||
<input
|
||||
type="checkbox"
|
||||
@@ -316,16 +285,54 @@ defmodule PinchflatWeb.CoreComponents do
|
||||
name={@name}
|
||||
value="true"
|
||||
checked={@checked}
|
||||
class="rounded border-zinc-300 text-zinc-900 focus:ring-0"
|
||||
class="rounded focus:ring-0"
|
||||
{@rest}
|
||||
/>
|
||||
<%= @label %>
|
||||
</label>
|
||||
<.help :if={@help}><%= @help %></.help>
|
||||
<.error :for={msg <- @errors}><%= msg %></.error>
|
||||
</div>
|
||||
"""
|
||||
end
|
||||
|
||||
def input(%{type: "toggle"} = assigns) do
|
||||
assigns =
|
||||
assign_new(assigns, :checked, fn ->
|
||||
Phoenix.HTML.Form.normalize_value("checkbox", assigns[:value])
|
||||
end)
|
||||
|
||||
~H"""
|
||||
<div x-data={"{ enabled: #{@checked}}"}>
|
||||
<.label for={@id}><%= @label %></.label>
|
||||
<div class="relative">
|
||||
<input type="hidden" name={@name} value="false" />
|
||||
<input
|
||||
type="checkbox"
|
||||
id={@id}
|
||||
name={@name}
|
||||
value="true"
|
||||
x-bind:checked="enabled"
|
||||
class="sr-only"
|
||||
@change="enabled = !enabled"
|
||||
{@rest}
|
||||
/>
|
||||
<div class="inline-block cursor-pointer" @click="enabled = !enabled">
|
||||
<div x-bind:class="enabled && '!bg-primary'" class="block h-8 w-14 rounded-full bg-black">
|
||||
</div>
|
||||
<div
|
||||
x-bind:class="enabled && '!right-1 !translate-x-full'"
|
||||
class="absolute left-1 top-1 flex h-6 w-6 items-center justify-center rounded-full bg-white transition"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<.help :if={@help}><%= @help %></.help>
|
||||
<.error :for={msg <- @errors}><%= msg %></.error>
|
||||
</div>
|
||||
</div>
|
||||
"""
|
||||
end
|
||||
|
||||
def input(%{type: "select"} = assigns) do
|
||||
~H"""
|
||||
<div phx-feedback-for={@name}>
|
||||
@@ -333,13 +340,17 @@ defmodule PinchflatWeb.CoreComponents do
|
||||
<select
|
||||
id={@id}
|
||||
name={@name}
|
||||
class="mt-2 block w-full rounded-md border border-gray-300 bg-white shadow-sm focus:border-zinc-400 focus:ring-0 sm:text-sm"
|
||||
class={[
|
||||
"relative z-20 w-full appearance-none rounded border border-stroke bg-transparent py-3 pl-5 pr-12 outline-none transition",
|
||||
"focus:border-primary active:border-primary dark:border-form-strokedark dark:bg-form-input text-black dark:text-white"
|
||||
]}
|
||||
multiple={@multiple}
|
||||
{@rest}
|
||||
>
|
||||
<option :if={@prompt} value=""><%= @prompt %></option>
|
||||
<%= Phoenix.HTML.Form.options_for_select(@options, @value) %>
|
||||
</select>
|
||||
<.help :if={@help}><%= @help %></.help>
|
||||
<.error :for={msg <- @errors}><%= msg %></.error>
|
||||
</div>
|
||||
"""
|
||||
@@ -360,6 +371,7 @@ defmodule PinchflatWeb.CoreComponents do
|
||||
]}
|
||||
{@rest}
|
||||
><%= Phoenix.HTML.Form.normalize_value("textarea", @value) %></textarea>
|
||||
<.help :if={@help}><%= @help %></.help>
|
||||
<.error :for={msg <- @errors}><%= msg %></.error>
|
||||
</div>
|
||||
"""
|
||||
@@ -376,18 +388,32 @@ defmodule PinchflatWeb.CoreComponents do
|
||||
id={@id}
|
||||
value={Phoenix.HTML.Form.normalize_value(@type, @value)}
|
||||
class={[
|
||||
"mt-2 block w-full rounded-lg text-zinc-900 focus:ring-0 sm:text-sm sm:leading-6",
|
||||
"phx-no-feedback:border-zinc-300 phx-no-feedback:focus:border-zinc-400",
|
||||
@errors == [] && "border-zinc-300 focus:border-zinc-400",
|
||||
"w-full rounded-lg border-[1.5px] border-stroke bg-transparent px-5 py-3 font-normal text-black",
|
||||
"outline-none transition focus:border-primary active:border-primary disabled:cursor-default disabled:bg-whiter",
|
||||
"dark:border-form-strokedark dark:bg-form-input dark:text-white dark:focus:border-primary",
|
||||
@errors != [] && "border-rose-400 focus:border-rose-400"
|
||||
]}
|
||||
{@rest}
|
||||
/>
|
||||
<.help :if={@help}><%= @help %></.help>
|
||||
<.error :for={msg <- @errors}><%= msg %></.error>
|
||||
</div>
|
||||
"""
|
||||
end
|
||||
|
||||
@doc """
|
||||
Renders help text.
|
||||
"""
|
||||
slot :inner_block, required: true
|
||||
|
||||
def help(assigns) do
|
||||
~H"""
|
||||
<p class="mt-1 text-sm leading-5">
|
||||
<%= render_slot(@inner_block) %>
|
||||
</p>
|
||||
"""
|
||||
end
|
||||
|
||||
@doc """
|
||||
Renders a label.
|
||||
"""
|
||||
@@ -396,7 +422,7 @@ defmodule PinchflatWeb.CoreComponents do
|
||||
|
||||
def label(assigns) do
|
||||
~H"""
|
||||
<label for={@for} class="block text-sm font-semibold leading-6 text-zinc-800">
|
||||
<label for={@for} class="mt-5 mb-2 block text-md font-medium text-black dark:text-white">
|
||||
<%= render_slot(@inner_block) %>
|
||||
</label>
|
||||
"""
|
||||
@@ -409,7 +435,7 @@ defmodule PinchflatWeb.CoreComponents do
|
||||
|
||||
def error(assigns) do
|
||||
~H"""
|
||||
<p class="mt-3 flex gap-3 text-sm leading-6 text-rose-600 phx-no-feedback:hidden">
|
||||
<p class="mt-1 mb-5 flex gap-3 text-md leading-6 text-rose-600 phx-no-feedback:hidden">
|
||||
<.icon name="hero-exclamation-circle-mini" class="mt-0.5 h-5 w-5 flex-none" />
|
||||
<%= render_slot(@inner_block) %>
|
||||
</p>
|
||||
@@ -425,7 +451,7 @@ defmodule PinchflatWeb.CoreComponents do
|
||||
slot :subtitle
|
||||
slot :actions
|
||||
|
||||
def header(assigns) do
|
||||
def old_header(assigns) do
|
||||
~H"""
|
||||
<header class={[@actions != [] && "flex items-center justify-between gap-6", @class]}>
|
||||
<div>
|
||||
@@ -446,10 +472,10 @@ defmodule PinchflatWeb.CoreComponents do
|
||||
|
||||
## Examples
|
||||
|
||||
<.table id="users" rows={@users}>
|
||||
<.old_table id="users" rows={@users}>
|
||||
<:col :let={user} label="id"><%= user.id %></:col>
|
||||
<:col :let={user} label="username"><%= user.username %></:col>
|
||||
</.table>
|
||||
</.old_table>
|
||||
"""
|
||||
attr :id, :string, required: true
|
||||
attr :rows, :list, required: true
|
||||
@@ -466,56 +492,54 @@ defmodule PinchflatWeb.CoreComponents do
|
||||
|
||||
slot :action, doc: "the slot for showing user actions in the last table column"
|
||||
|
||||
def table(assigns) do
|
||||
def old_table(assigns) do
|
||||
assigns =
|
||||
with %{rows: %Phoenix.LiveView.LiveStream{}} <- assigns do
|
||||
assign(assigns, row_id: assigns.row_id || fn {id, _item} -> id end)
|
||||
end
|
||||
|
||||
~H"""
|
||||
<div class="overflow-y-auto px-4 sm:overflow-visible sm:px-0">
|
||||
<table class="w-[40rem] mt-11 sm:w-full">
|
||||
<thead class="text-sm text-left leading-6 text-zinc-500">
|
||||
<tr>
|
||||
<th :for={col <- @col} class="p-0 pb-4 pr-6 font-normal"><%= col[:label] %></th>
|
||||
<th :if={@action != []} class="relative p-0 pb-4">
|
||||
<span class="sr-only"><%= gettext("Actions") %></span>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody
|
||||
id={@id}
|
||||
phx-update={match?(%Phoenix.LiveView.LiveStream{}, @rows) && "stream"}
|
||||
class="relative divide-y divide-zinc-100 border-t border-zinc-200 text-sm leading-6 text-zinc-700"
|
||||
>
|
||||
<tr :for={row <- @rows} id={@row_id && @row_id.(row)} class="group hover:bg-zinc-50">
|
||||
<td
|
||||
:for={{col, i} <- Enum.with_index(@col)}
|
||||
phx-click={@row_click && @row_click.(row)}
|
||||
class={["relative p-0", @row_click && "hover:cursor-pointer"]}
|
||||
>
|
||||
<div class="block py-4 pr-6">
|
||||
<span class="absolute -inset-y-px right-0 -left-4 group-hover:bg-zinc-50 sm:rounded-l-xl" />
|
||||
<span class={["relative", i == 0 && "font-semibold text-zinc-900"]}>
|
||||
<%= render_slot(col, @row_item.(row)) %>
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td :if={@action != []} class="relative w-14 p-0">
|
||||
<div class="relative whitespace-nowrap py-4 text-right text-sm font-medium">
|
||||
<span class="absolute -inset-y-px -right-4 left-0 group-hover:bg-zinc-50 sm:rounded-r-xl" />
|
||||
<span
|
||||
:for={action <- @action}
|
||||
class="relative ml-4 font-semibold leading-6 text-zinc-900 hover:text-zinc-700"
|
||||
>
|
||||
<%= render_slot(action, @row_item.(row)) %>
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<table class="w-[40rem] mt-11 sm:w-full">
|
||||
<thead class="text-sm text-left leading-6 text-zinc-500">
|
||||
<tr>
|
||||
<th :for={col <- @col} class="p-0 pb-4 pr-6 font-normal"><%= col[:label] %></th>
|
||||
<th :if={@action != []} class="relative p-0 pb-4">
|
||||
<span class="sr-only"><%= gettext("Actions") %></span>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody
|
||||
id={@id}
|
||||
phx-update={match?(%Phoenix.LiveView.LiveStream{}, @rows) && "stream"}
|
||||
class="relative divide-y divide-zinc-100 border-t border-zinc-200 text-sm leading-6 text-zinc-700"
|
||||
>
|
||||
<tr :for={row <- @rows} id={@row_id && @row_id.(row)} class="group hover:bg-zinc-50">
|
||||
<td
|
||||
:for={{col, i} <- Enum.with_index(@col)}
|
||||
phx-click={@row_click && @row_click.(row)}
|
||||
class={["relative p-0", @row_click && "hover:cursor-pointer"]}
|
||||
>
|
||||
<div class="block py-4 pr-6">
|
||||
<span class="absolute -inset-y-px right-0 -left-4 group-hover:bg-zinc-50 sm:rounded-l-xl" />
|
||||
<span class={["relative", i == 0 && "font-semibold text-zinc-900"]}>
|
||||
<%= render_slot(col, @row_item.(row)) %>
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td :if={@action != []} class="relative w-14 p-0">
|
||||
<div class="relative whitespace-nowrap py-4 text-right text-sm font-medium">
|
||||
<span class="absolute -inset-y-px -right-4 left-0 group-hover:bg-zinc-50 sm:rounded-r-xl" />
|
||||
<span
|
||||
:for={action <- @action}
|
||||
class="relative ml-4 font-semibold leading-6 text-zinc-900 hover:text-zinc-700"
|
||||
>
|
||||
<%= render_slot(action, @row_item.(row)) %>
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
"""
|
||||
end
|
||||
|
||||
@@ -536,10 +560,10 @@ defmodule PinchflatWeb.CoreComponents do
|
||||
def list(assigns) do
|
||||
~H"""
|
||||
<div class="mt-2 mb-14">
|
||||
<dl class="-my-4 divide-y divide-zinc-100">
|
||||
<dl class="-my-4 divide-y dark:divide-strokedark">
|
||||
<div :for={item <- @item} class="flex gap-4 py-4 text-sm leading-6 sm:gap-8">
|
||||
<dt class="w-1/4 flex-none text-zinc-500"><%= item.title %></dt>
|
||||
<dd class="text-zinc-700"><%= render_slot(item) %></dd>
|
||||
<dt class="w-1/4 flex-none dark:text-white"><%= item.title %></dt>
|
||||
<dd class="dark:text-white"><%= render_slot(item) %></dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
defmodule PinchflatWeb.CustomComponents.ButtonComponents do
|
||||
@moduledoc false
|
||||
use Phoenix.Component
|
||||
|
||||
@doc """
|
||||
Render a button
|
||||
|
||||
## Examples
|
||||
|
||||
<.button color="bg-primary" rounding="rounded-sm">
|
||||
<span>Click me</span>
|
||||
</.button>
|
||||
"""
|
||||
attr :color, :string, default: "bg-primary"
|
||||
attr :rounding, :string, default: "rounded-sm"
|
||||
attr :class, :string, default: ""
|
||||
|
||||
slot :inner_block, required: true
|
||||
|
||||
def button(assigns) do
|
||||
~H"""
|
||||
<button class={[
|
||||
"text-center font-medium text-white",
|
||||
"#{@rounding} inline-flex items-center justify-center px-10 py-4",
|
||||
"#{@color}",
|
||||
"hover:bg-opacity-90 lg:px-8 xl:px-10",
|
||||
@class
|
||||
]}>
|
||||
<%= render_slot(@inner_block) %>
|
||||
</button>
|
||||
"""
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,53 @@
|
||||
defmodule PinchflatWeb.CustomComponents.TableComponents do
|
||||
@moduledoc false
|
||||
use Phoenix.Component
|
||||
|
||||
@doc """
|
||||
Renders a table component with the given rows and columns.
|
||||
|
||||
## Examples
|
||||
|
||||
<.table rows={@users}>
|
||||
<:col :let={user} label="Name"><%= user.name %></:col>
|
||||
</.table>
|
||||
"""
|
||||
attr :rows, :list, required: true
|
||||
attr :table_class, :string, default: ""
|
||||
|
||||
attr :row_item, :any,
|
||||
default: &Function.identity/1,
|
||||
doc: "the function for mapping each row before calling the :col and :action slots"
|
||||
|
||||
slot :col, required: true do
|
||||
attr :label, :string
|
||||
attr :class, :string
|
||||
end
|
||||
|
||||
def table(assigns) do
|
||||
~H"""
|
||||
<table class={["w-full table-auto", @table_class]}>
|
||||
<thead>
|
||||
<tr class="bg-gray-2 text-left dark:bg-meta-4">
|
||||
<th :for={col <- @col} class="px-4 py-4 font-medium text-black dark:text-white xl:pl-11">
|
||||
<%= col[:label] %>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr :for={{row, i} <- Enum.with_index(@rows)}>
|
||||
<td
|
||||
:for={col <- @col}
|
||||
class={[
|
||||
"px-4 py-5 pl-9 dark:border-strokedark xl:pl-11",
|
||||
i + 1 > length(@rows) && "border-b border-[#eee] dark:border-π",
|
||||
col[:class]
|
||||
]}
|
||||
>
|
||||
<%= render_slot(col, @row_item.(row)) %>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
"""
|
||||
end
|
||||
end
|
||||
@@ -2,4 +2,28 @@ defmodule PinchflatWeb.Layouts do
|
||||
use PinchflatWeb, :html
|
||||
|
||||
embed_templates "layouts/*"
|
||||
embed_templates "layouts/partials/*"
|
||||
|
||||
attr :icon, :string, required: true
|
||||
attr :text, :string, required: true
|
||||
attr :navigate, :any, required: true
|
||||
|
||||
def sidebar_item(assigns) do
|
||||
# I'm testing out grouping classes here. Tentative order: font, layout, color, animation, state-modifiers
|
||||
~H"""
|
||||
<li>
|
||||
<.link
|
||||
navigate={@navigate}
|
||||
class={[
|
||||
"font-medium text-bodydark1",
|
||||
"group relative flex items-center gap-2.5 rounded-sm px-4 py-2 duration-300 ease-in-out",
|
||||
"duration-300 ease-in-out",
|
||||
"hover:bg-graydark dark:hover:bg-meta-4"
|
||||
]}
|
||||
>
|
||||
<.icon name={@icon} /> <%= @text %>
|
||||
</.link>
|
||||
</li>
|
||||
"""
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,32 +1,13 @@
|
||||
<header class="px-4 sm:px-6 lg:px-8">
|
||||
<div class="flex items-center justify-between border-b border-zinc-100 py-3 text-sm">
|
||||
<div class="flex items-center gap-4">
|
||||
<a href="/">
|
||||
<img src={~p"/images/logo.svg"} width="36" />
|
||||
</a>
|
||||
<p class="bg-brand/5 text-brand rounded-full px-2 font-medium leading-6">
|
||||
v<%= Application.spec(:phoenix, :vsn) %>
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex items-center gap-4 font-semibold leading-6 text-zinc-900">
|
||||
<a href="https://twitter.com/elixirphoenix" class="hover:text-zinc-700">
|
||||
@elixirphoenix
|
||||
</a>
|
||||
<a href="https://github.com/phoenixframework/phoenix" class="hover:text-zinc-700">
|
||||
GitHub
|
||||
</a>
|
||||
<a
|
||||
href="https://hexdocs.pm/phoenix/overview.html"
|
||||
class="rounded-lg bg-zinc-100 px-2 py-1 hover:bg-zinc-200/80"
|
||||
>
|
||||
Get Started <span aria-hidden="true">→</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="flex h-screen overflow-hidden">
|
||||
<.sidebar />
|
||||
|
||||
<div class="relative flex flex-1 flex-col overflow-y-auto overflow-x-hidden">
|
||||
<.header />
|
||||
<main>
|
||||
<div class="mx-auto max-w-screen-2xl p-4 md:p-6 2xl:p-10">
|
||||
<.flash_group flash={@flash} />
|
||||
<%= @inner_content %>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</header>
|
||||
<main class="px-4 py-20 sm:px-6 lg:px-8">
|
||||
<div class="mx-auto max-w-2xl">
|
||||
<.flash_group flash={@flash} />
|
||||
<%= @inner_content %>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
<header class="sticky top-0 z-999 flex w-full bg-white drop-shadow-1 dark:bg-boxdark dark:drop-shadow-none">
|
||||
<div class="flex flex-grow items-center justify-end px-4 py-4 shadow-2 md:px-6 2xl:px-11">
|
||||
<div class="flex items-center gap-2 sm:gap-4 lg:hidden">
|
||||
<button
|
||||
class="z-99999 block rounded-sm border border-stroke bg-white p-1.5 shadow-sm dark:border-strokedark dark:bg-boxdark lg:hidden"
|
||||
@click.stop="sidebarToggle = !sidebarToggle"
|
||||
>
|
||||
<.icon name="hero-bars-3" />
|
||||
</button>
|
||||
<a class="block flex-shrink-0 lg:hidden" href="#">
|
||||
<h2 class="text-title-md2 font-bold text-white">Pinchflat</h2>
|
||||
</a>
|
||||
</div>
|
||||
<div class="hidden sm:block bg-meta-4 rounded-md">
|
||||
<%!-- Aspirational (for now) --%>
|
||||
<div class="relative">
|
||||
<span class="absolute left-2 top-1/2 -translate-y-1/2 flex">
|
||||
<.icon name="hero-magnifying-glass" />
|
||||
</span>
|
||||
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Type to search..."
|
||||
class="w-full bg-transparent pl-9 pr-4 border-0 focus:ring-0 focus:outline-none xl:w-125"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
@@ -0,0 +1,29 @@
|
||||
<aside
|
||||
x-bind:class="sidebarToggle ? 'translate-x-0' : '-translate-x-full'"
|
||||
class="absolute left-0 top-0 z-9999 flex h-screen w-72.5 flex-col overflow-y-hidden bg-black duration-300 ease-linear dark:bg-boxdark lg:static lg:translate-x-0"
|
||||
@click.outside="sidebarToggle = false"
|
||||
>
|
||||
<div class="flex items-center justify-between gap-2 px-6 py-5.5 lg:py-6.5">
|
||||
<h2 class="text-title-md2 font-bold text-white">Pinchflat</h2>
|
||||
|
||||
<button class="block lg:hidden" @click.stop="sidebarToggle = !sidebarToggle">
|
||||
<.icon name="hero-arrow-left" class="fill-current" />
|
||||
</button>
|
||||
</div>
|
||||
<div class="no-scrollbar flex flex-col overflow-y-auto duration-300 ease-linear">
|
||||
<nav class="mt-5 px-4 py-4 lg:mt-9 lg:px-6">
|
||||
<div>
|
||||
<h3 class="mb-4 ml-4 text-sm font-medium text-bodydark2">MENU</h3>
|
||||
<ul class="mb-6 flex flex-col gap-1.5">
|
||||
<.sidebar_item icon="hero-tv" text="Sources" navigate={~p"/sources"} />
|
||||
|
||||
<.sidebar_item
|
||||
icon="hero-adjustments-vertical"
|
||||
text="Media Profiles"
|
||||
navigate={~p"/media_profiles"}
|
||||
/>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
</aside>
|
||||
@@ -1,5 +1,5 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" class="[scrollbar-gutter:stable]">
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
@@ -11,7 +11,7 @@
|
||||
<script defer phx-track-static type="text/javascript" src={~p"/assets/app.js"}>
|
||||
</script>
|
||||
</head>
|
||||
<body class="bg-white antialiased">
|
||||
<body x-data="{ 'sidebarToggle': false }" class="dark text-bodydark bg-boxdark-2">
|
||||
<%= @inner_content %>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
<.header>
|
||||
Edit Media profile <%= @media_profile.id %>
|
||||
<:subtitle>Use this form to manage media_profile records in your database.</:subtitle>
|
||||
</.header>
|
||||
<div class="mb-6 flex gap-3 flex-row items-center">
|
||||
<.link navigate={~p"/media_profiles"}>
|
||||
<.icon name="hero-arrow-left" class="w-10 h-10 hover:dark:text-white" />
|
||||
</.link>
|
||||
<h2 class="text-title-md2 font-bold text-black dark:text-white ml-4">Edit Media Profile</h2>
|
||||
</div>
|
||||
|
||||
<.media_profile_form changeset={@changeset} action={~p"/media_profiles/#{@media_profile}"} />
|
||||
|
||||
<.back navigate={~p"/media_profiles"}>Back to media_profiles</.back>
|
||||
<div class="rounded-sm border border-stroke bg-white px-5 pb-2.5 pt-6 shadow-default dark:border-strokedark dark:bg-boxdark sm:px-7.5 xl:pb-1">
|
||||
<div class="max-w-full overflow-x-auto">
|
||||
<div class="flex flex-col gap-10">
|
||||
<.media_profile_form changeset={@changeset} action={~p"/media_profiles/#{@media_profile}"} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,30 +1,39 @@
|
||||
<.header>
|
||||
Listing Media profiles
|
||||
<:actions>
|
||||
<.link href={~p"/media_profiles/new"}>
|
||||
<.button>New Media profile</.button>
|
||||
<div class="mb-6 flex gap-3 flex-row items-center justify-between">
|
||||
<h2 class="text-title-md2 font-bold text-black dark:text-white">All Media Profiles</h2>
|
||||
<nav>
|
||||
<.link navigate={~p"/media_profiles/new"}>
|
||||
<.button color="bg-primary" rounding="rounded-full">
|
||||
<span class="font-bold mx-2">+</span> New Media Profile
|
||||
</.button>
|
||||
</.link>
|
||||
</:actions>
|
||||
</.header>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<.table
|
||||
id="media_profiles"
|
||||
rows={@media_profiles}
|
||||
row_click={&JS.navigate(~p"/media_profiles/#{&1}")}
|
||||
>
|
||||
<:col :let={media_profile} label="Name"><%= media_profile.name %></:col>
|
||||
<:col :let={media_profile} label="Output path template">
|
||||
<%= media_profile.output_path_template %>
|
||||
</:col>
|
||||
<:action :let={media_profile}>
|
||||
<div class="sr-only">
|
||||
<.link navigate={~p"/media_profiles/#{media_profile}"}>Show</.link>
|
||||
<div class="rounded-sm border border-stroke bg-white shadow-default dark:border-strokedark dark:bg-boxdark">
|
||||
<div class="max-w-full overflow-x-auto">
|
||||
<div class="flex flex-col gap-10">
|
||||
<.table rows={@media_profiles} table_class="text-black dark:text-white">
|
||||
<:col :let={media_profile} label="Name">
|
||||
<%= media_profile.name %>
|
||||
</:col>
|
||||
<:col :let={media_profile} label="Output Template">
|
||||
<code class="text-sm"><%= media_profile.output_path_template %></code>
|
||||
</:col>
|
||||
<:col :let={media_profile} label="" class="flex place-content-evenly">
|
||||
<.link
|
||||
navigate={~p"/media_profiles/#{media_profile.id}"}
|
||||
class="hover:text-secondary duration-200 ease-in-out mx-0.5"
|
||||
>
|
||||
<.icon name="hero-eye" />
|
||||
</.link>
|
||||
<.link
|
||||
navigate={~p"/media_profiles/#{media_profile.id}/edit"}
|
||||
class="hover:text-secondary duration-200 ease-in-out mx-0.5"
|
||||
>
|
||||
<.icon name="hero-pencil-square" />
|
||||
</.link>
|
||||
</:col>
|
||||
</.table>
|
||||
</div>
|
||||
<.link navigate={~p"/media_profiles/#{media_profile}/edit"}>Edit</.link>
|
||||
</:action>
|
||||
<:action :let={media_profile}>
|
||||
<.link href={~p"/media_profiles/#{media_profile}"} method="delete" data-confirm="Are you sure?">
|
||||
Delete
|
||||
</.link>
|
||||
</:action>
|
||||
</.table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+48
-15
@@ -2,29 +2,62 @@
|
||||
<.error :if={@changeset.action}>
|
||||
Oops, something went wrong! Please check the errors below.
|
||||
</.error>
|
||||
<.input field={f[:name]} type="text" label="Name" />
|
||||
<.input field={f[:output_path_template]} type="text" label="Output path template" />
|
||||
<h3 class="my-4 text-2xl text-black dark:text-white">
|
||||
General Options
|
||||
</h3>
|
||||
<.input field={f[:name]} type="text" label="Name" placeholder="New Profile" help="(required)" />
|
||||
<.input
|
||||
field={f[:output_path_template]}
|
||||
type="text"
|
||||
label="Output path template"
|
||||
help="TODO: provide docs (required)"
|
||||
/>
|
||||
|
||||
<h3>Subtitle Options</h3>
|
||||
<.input field={f[:download_subs]} type="checkbox" label="Download Subs" />
|
||||
<.input field={f[:download_auto_subs]} type="checkbox" label="Download Autogenerated Subs" />
|
||||
<.input field={f[:embed_subs]} type="checkbox" label="Embed Subs" />
|
||||
<.input field={f[:sub_langs]} type="text" label="Sub Langs" />
|
||||
<h3 class="mb-4 mt-8 text-2xl text-black dark:text-white">
|
||||
Subtitle Options
|
||||
</h3>
|
||||
<.input field={f[:download_subs]} type="toggle" label="Download Subtitles" />
|
||||
<.input
|
||||
field={f[:download_auto_subs]}
|
||||
type="toggle"
|
||||
label="Download Autogenerated Subtitles"
|
||||
help="Prefers normal subs but will download autogenerated if needed"
|
||||
/>
|
||||
<.input
|
||||
field={f[:embed_subs]}
|
||||
type="toggle"
|
||||
label="Embed Subtitles"
|
||||
help="Embeds subtitles in the video file itself, if supported"
|
||||
/>
|
||||
<.input
|
||||
field={f[:sub_langs]}
|
||||
type="text"
|
||||
label="Subtitle Languages"
|
||||
help="Use commas for multiple languages (eg: en,de)"
|
||||
/>
|
||||
|
||||
<h3>Thumbnail Options</h3>
|
||||
<.input field={f[:download_thumbnail]} type="checkbox" label="Download Thumbnail" />
|
||||
<.input field={f[:embed_thumbnail]} type="checkbox" label="Embed Thumbnail" />
|
||||
<h3 class="mb-4 mt-8 text-2xl text-black dark:text-white">
|
||||
Thumbnail Options
|
||||
</h3>
|
||||
<.input field={f[:download_thumbnail]} type="toggle" label="Download Thumbnail" />
|
||||
<.input field={f[:embed_thumbnail]} type="toggle" label="Embed Thumbnail" />
|
||||
|
||||
<h3>Metadata Options</h3>
|
||||
<.input field={f[:download_metadata]} type="checkbox" label="Download Metadata" />
|
||||
<.input field={f[:embed_metadata]} type="checkbox" label="Embed Metadata" />
|
||||
<h3 class="mb-4 mt-8 text-2xl text-black dark:text-white">
|
||||
Metadata Options
|
||||
</h3>
|
||||
<.input field={f[:download_metadata]} type="toggle" label="Download Metadata" />
|
||||
<.input field={f[:embed_metadata]} type="toggle" label="Embed Metadata" />
|
||||
|
||||
<h3 class="mb-4 mt-8 text-2xl text-black dark:text-white">
|
||||
Release Format Options
|
||||
</h3>
|
||||
|
||||
<h3>Release Format Options</h3>
|
||||
<.input
|
||||
field={f[:shorts_behaviour]}
|
||||
options={friendly_format_type_options()}
|
||||
type="select"
|
||||
label="Include Shorts?"
|
||||
help="Experimental"
|
||||
/>
|
||||
<.input
|
||||
field={f[:livestream_behaviour]}
|
||||
@@ -34,6 +67,6 @@
|
||||
/>
|
||||
|
||||
<:actions>
|
||||
<.button>Save Media profile</.button>
|
||||
<.button class="mt-15 mb-5 sm:mb-7.5">Save Media profile</.button>
|
||||
</:actions>
|
||||
</.simple_form>
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
<.header>
|
||||
New Media profile
|
||||
<:subtitle>Use this form to manage media_profile records in your database.</:subtitle>
|
||||
</.header>
|
||||
<div class="mb-6 flex gap-3 flex-row items-center">
|
||||
<.link navigate={~p"/media_profiles"}>
|
||||
<.icon name="hero-arrow-left" class="w-10 h-10 hover:dark:text-white" />
|
||||
</.link>
|
||||
<h2 class="text-title-md2 font-bold text-black dark:text-white ml-4">New Media Profile</h2>
|
||||
</div>
|
||||
|
||||
<.media_profile_form changeset={@changeset} action={~p"/media_profiles"} />
|
||||
|
||||
<.back navigate={~p"/media_profiles"}>Back to media_profiles</.back>
|
||||
<div class="rounded-sm border border-stroke bg-white px-5 pb-2.5 pt-6 shadow-default dark:border-strokedark dark:bg-boxdark sm:px-7.5 xl:pb-1">
|
||||
<div class="max-w-full overflow-x-auto">
|
||||
<div class="flex flex-col gap-10">
|
||||
<.media_profile_form changeset={@changeset} action={~p"/media_profiles"} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,13 +1,26 @@
|
||||
<.header>
|
||||
Media profile <%= @media_profile.id %>
|
||||
<:subtitle>This is a media_profile record from your database.</:subtitle>
|
||||
<:actions>
|
||||
<.link href={~p"/media_profiles/#{@media_profile}/edit"}>
|
||||
<.button>Edit media_profile</.button>
|
||||
<div class="mb-6 flex gap-3 flex-row items-center justify-between">
|
||||
<div class="flex gap-3 items-center">
|
||||
<.link navigate={~p"/media_profiles"}>
|
||||
<.icon name="hero-arrow-left" class="w-10 h-10 hover:dark:text-white" />
|
||||
</.link>
|
||||
</:actions>
|
||||
</.header>
|
||||
<h2 class="text-title-md2 font-bold text-black dark:text-white ml-4">
|
||||
Media Profile #<%= @media_profile.id %>
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<.list_items_from_map map={Map.from_struct(@media_profile)} />
|
||||
|
||||
<.back navigate={~p"/media_profiles"}>Back to media_profiles</.back>
|
||||
<nav>
|
||||
<.link navigate={~p"/media_profiles/#{@media_profile}/edit"}>
|
||||
<.button color="bg-primary" rounding="rounded-full">
|
||||
<.icon name="hero-pencil-square" class="mr-2" /> Edit Media Profile
|
||||
</.button>
|
||||
</.link>
|
||||
</nav>
|
||||
</div>
|
||||
<div class="rounded-sm border border-stroke bg-white px-5 pb-2.5 pt-6 shadow-default dark:border-strokedark dark:bg-boxdark sm:px-7.5 xl:pb-1">
|
||||
<div class="max-w-full overflow-x-auto">
|
||||
<div class="flex flex-col gap-10 dark:text-white">
|
||||
<h3 class="font-bold text-xl">Attributes</h3>
|
||||
<.list_items_from_map map={Map.from_struct(@media_profile)} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -7,7 +7,7 @@ defmodule PinchflatWeb.MediaSources.SourceController do
|
||||
alias Pinchflat.MediaSource.Source
|
||||
|
||||
def index(conn, _params) do
|
||||
sources = MediaSource.list_sources()
|
||||
sources = Repo.preload(MediaSource.list_sources(), :media_profile)
|
||||
|
||||
render(conn, :index, sources: sources)
|
||||
end
|
||||
@@ -23,7 +23,7 @@ defmodule PinchflatWeb.MediaSources.SourceController do
|
||||
{:ok, source} ->
|
||||
conn
|
||||
|> put_flash(:info, "Source created successfully.")
|
||||
|> redirect(to: ~p"/media_sources/sources/#{source}")
|
||||
|> redirect(to: ~p"/sources/#{source}")
|
||||
|
||||
{:error, %Ecto.Changeset{} = changeset} ->
|
||||
render(conn, :new, changeset: changeset, media_profiles: media_profiles())
|
||||
@@ -53,7 +53,7 @@ defmodule PinchflatWeb.MediaSources.SourceController do
|
||||
{:ok, source} ->
|
||||
conn
|
||||
|> put_flash(:info, "Source updated successfully.")
|
||||
|> redirect(to: ~p"/media_sources/sources/#{source}")
|
||||
|> redirect(to: ~p"/sources/#{source}")
|
||||
|
||||
{:error, %Ecto.Changeset{} = changeset} ->
|
||||
render(conn, :edit,
|
||||
@@ -70,7 +70,7 @@ defmodule PinchflatWeb.MediaSources.SourceController do
|
||||
|
||||
conn
|
||||
|> put_flash(:info, "Source deleted successfully.")
|
||||
|> redirect(to: ~p"/media_sources/sources")
|
||||
|> redirect(to: ~p"/sources")
|
||||
end
|
||||
|
||||
defp media_profiles do
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
<.header>
|
||||
Edit Source <%= @source.id %>
|
||||
<:subtitle>Use this form to manage source records in your database.</:subtitle>
|
||||
</.header>
|
||||
<div class="mb-6 flex gap-3 flex-row items-center">
|
||||
<.link navigate={~p"/sources"}>
|
||||
<.icon name="hero-arrow-left" class="w-10 h-10 hover:dark:text-white" />
|
||||
</.link>
|
||||
<h2 class="text-title-md2 font-bold text-black dark:text-white ml-4">Edit Source</h2>
|
||||
</div>
|
||||
|
||||
<.source_form
|
||||
changeset={@changeset}
|
||||
media_profiles={@media_profiles}
|
||||
action={~p"/media_sources/sources/#{@source}"}
|
||||
/>
|
||||
|
||||
<.back navigate={~p"/media_sources/sources"}>Back to sources</.back>
|
||||
<div class="rounded-sm border border-stroke bg-white px-5 pb-2.5 pt-6 shadow-default dark:border-strokedark dark:bg-boxdark sm:px-7.5 xl:pb-1">
|
||||
<div class="max-w-full overflow-x-auto">
|
||||
<div class="flex flex-col gap-10">
|
||||
<.source_form
|
||||
changeset={@changeset}
|
||||
media_profiles={@media_profiles}
|
||||
action={~p"/sources/#{@source}"}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,24 +1,48 @@
|
||||
<.header>
|
||||
Listing Sources
|
||||
<:actions>
|
||||
<.link href={~p"/media_sources/sources/new"}>
|
||||
<.button>New Source</.button>
|
||||
<div class="mb-6 flex gap-3 flex-row items-center justify-between">
|
||||
<h2 class="text-title-md2 font-bold text-black dark:text-white">All Sources</h2>
|
||||
<nav>
|
||||
<.link navigate={~p"/sources/new"}>
|
||||
<.button color="bg-primary" rounding="rounded-full">
|
||||
<span class="font-bold mx-2">+</span> New Source
|
||||
</.button>
|
||||
</.link>
|
||||
</:actions>
|
||||
</.header>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<.table id="sources" rows={@sources} row_click={&JS.navigate(~p"/media_sources/sources/#{&1}")}>
|
||||
<:col :let={source} label="Collection Name"><%= source.collection_name %></:col>
|
||||
<:col :let={source} label="Collection ID"><%= source.collection_id %></:col>
|
||||
<:action :let={source}>
|
||||
<div class="sr-only">
|
||||
<.link navigate={~p"/media_sources/sources/#{source}"}>Show</.link>
|
||||
<div class="rounded-sm border border-stroke bg-white shadow-default dark:border-strokedark dark:bg-boxdark">
|
||||
<div class="max-w-full overflow-x-auto">
|
||||
<div class="flex flex-col gap-10">
|
||||
<.table rows={@sources} table_class="text-black dark:text-white">
|
||||
<:col :let={source} label="Name">
|
||||
<%= source.friendly_name || source.collection_name %>
|
||||
</:col>
|
||||
<:col :let={source} label="Type"><%= source.collection_type %></:col>
|
||||
<:col :let={source} label="Should Download?">
|
||||
<.icon name={if source.download_media, do: "hero-check", else: "hero-x-mark"} />
|
||||
</:col>
|
||||
<:col :let={source} label="Media Profile">
|
||||
<.link
|
||||
navigate={~p"/media_profiles/#{source.media_profile_id}"}
|
||||
class="hover:text-secondary duration-200 ease-in-out"
|
||||
>
|
||||
<%= source.media_profile.name %>
|
||||
</.link>
|
||||
</:col>
|
||||
<:col :let={source} label="" class="flex place-content-evenly">
|
||||
<.link
|
||||
navigate={~p"/sources/#{source.id}"}
|
||||
class="hover:text-secondary duration-200 ease-in-out mx-0.5"
|
||||
>
|
||||
<.icon name="hero-eye" />
|
||||
</.link>
|
||||
<.link
|
||||
navigate={~p"/sources/#{source.id}/edit"}
|
||||
class="hover:text-secondary duration-200 ease-in-out mx-0.5"
|
||||
>
|
||||
<.icon name="hero-pencil-square" />
|
||||
</.link>
|
||||
</:col>
|
||||
</.table>
|
||||
</div>
|
||||
<.link navigate={~p"/media_sources/sources/#{source}/edit"}>Edit</.link>
|
||||
</:action>
|
||||
<:action :let={source}>
|
||||
<.link href={~p"/media_sources/sources/#{source}"} method="delete" data-confirm="Are you sure?">
|
||||
Delete
|
||||
</.link>
|
||||
</:action>
|
||||
</.table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
<.header>
|
||||
New Source
|
||||
<:subtitle>Use this form to manage source records in your database.</:subtitle>
|
||||
</.header>
|
||||
<div class="mb-6 flex gap-3 flex-row items-center">
|
||||
<.link navigate={~p"/sources"}>
|
||||
<.icon name="hero-arrow-left" class="w-10 h-10 hover:dark:text-white" />
|
||||
</.link>
|
||||
<h2 class="text-title-md2 font-bold text-black dark:text-white ml-4">New Source</h2>
|
||||
</div>
|
||||
|
||||
<.source_form
|
||||
changeset={@changeset}
|
||||
media_profiles={@media_profiles}
|
||||
action={~p"/media_sources/sources"}
|
||||
/>
|
||||
|
||||
<.back navigate={~p"/media_sources/sources"}>Back to sources</.back>
|
||||
<div class="rounded-sm border border-stroke bg-white px-5 pb-2.5 pt-6 shadow-default dark:border-strokedark dark:bg-boxdark sm:px-7.5 xl:pb-1">
|
||||
<div class="max-w-full overflow-x-auto">
|
||||
<div class="flex flex-col gap-10">
|
||||
<.source_form changeset={@changeset} media_profiles={@media_profiles} action={~p"/sources"} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,23 +1,38 @@
|
||||
<.header>
|
||||
Source <%= @source.id %>
|
||||
<:subtitle>This is a source record from your database.</:subtitle>
|
||||
<:actions>
|
||||
<.link href={~p"/media_sources/sources/#{@source}/edit"}>
|
||||
<.button>Edit source</.button>
|
||||
<div class="mb-6 flex gap-3 flex-row items-center justify-between">
|
||||
<div class="flex gap-3 items-center">
|
||||
<.link navigate={~p"/sources"}>
|
||||
<.icon name="hero-arrow-left" class="w-10 h-10 hover:dark:text-white" />
|
||||
</.link>
|
||||
</:actions>
|
||||
</.header>
|
||||
<h2 class="text-title-md2 font-bold text-black dark:text-white ml-4">
|
||||
Source #<%= @source.id %>
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<h3 class="mt-14">Relationships</h3>
|
||||
<.list>
|
||||
<:item title="media_profile">
|
||||
<.link href={~p"/media_profiles/#{@source.media_profile}"}>
|
||||
<%= @source.media_profile.name %>
|
||||
<nav>
|
||||
<.link navigate={~p"/sources/#{@source}/edit"}>
|
||||
<.button color="bg-primary" rounding="rounded-full">
|
||||
<.icon name="hero-pencil-square" class="mr-2" /> Edit Source
|
||||
</.button>
|
||||
</.link>
|
||||
</:item>
|
||||
</.list>
|
||||
</nav>
|
||||
</div>
|
||||
<div class="rounded-sm border border-stroke bg-white px-5 pb-2.5 pt-6 shadow-default dark:border-strokedark dark:bg-boxdark sm:px-7.5 xl:pb-1">
|
||||
<div class="max-w-full overflow-x-auto">
|
||||
<div class="flex flex-col gap-10 dark:text-white">
|
||||
<h3 class="mt-14 font-bold text-xl">Relationships</h3>
|
||||
<.list>
|
||||
<:item title="media_profile">
|
||||
<.link
|
||||
navigate={~p"/media_profiles/#{@source.media_profile_id}"}
|
||||
class="hover:text-secondary duration-200 ease-in-out"
|
||||
>
|
||||
<%= @source.media_profile.name %>
|
||||
</.link>
|
||||
</:item>
|
||||
</.list>
|
||||
|
||||
<h3>Attributes</h3>
|
||||
<.list_items_from_map map={Map.from_struct(@source)} />
|
||||
|
||||
<.back navigate={~p"/media_sources/sources"}>Back to sources</.back>
|
||||
<h3 class="font-bold text-xl">Attributes</h3>
|
||||
<.list_items_from_map map={Map.from_struct(@source)} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -3,6 +3,13 @@
|
||||
Oops, something went wrong! Please check the errors below.
|
||||
</.error>
|
||||
|
||||
<.input
|
||||
field={f[:original_url]}
|
||||
type="text"
|
||||
label="Source URL"
|
||||
help="URL of a channel or playlist (required)"
|
||||
/>
|
||||
|
||||
<.input field={f[:friendly_name]} type="text" label="Friendly Name" />
|
||||
|
||||
<.input
|
||||
@@ -16,21 +23,25 @@
|
||||
field={f[:collection_type]}
|
||||
options={friendly_collection_types()}
|
||||
type="select"
|
||||
label="Collection Type"
|
||||
label="Source Type"
|
||||
/>
|
||||
|
||||
<.input field={f[:original_url]} type="text" label="Source URL" />
|
||||
|
||||
<.input
|
||||
field={f[:index_frequency_minutes]}
|
||||
options={friendly_index_frequencies()}
|
||||
type="select"
|
||||
label="Index Frequency"
|
||||
help="Roughly how often to check for media to download"
|
||||
/>
|
||||
|
||||
<.input field={f[:download_media]} type="checkbox" label="Download Media?" />
|
||||
<.input
|
||||
field={f[:download_media]}
|
||||
type="toggle"
|
||||
label="Download Media?"
|
||||
help="Unchecking still indexes media but it won't be downloaded"
|
||||
/>
|
||||
|
||||
<:actions>
|
||||
<.button>Save Source</.button>
|
||||
<.button class="mt-15 mb-5 sm:mb-7.5">Save Source</.button>
|
||||
</:actions>
|
||||
</.simple_form>
|
||||
|
||||
@@ -4,6 +4,6 @@ defmodule PinchflatWeb.PageController do
|
||||
def home(conn, _params) do
|
||||
# The home page is often custom made,
|
||||
# so skip the default app layout.
|
||||
render(conn, :home, layout: false)
|
||||
render(conn, :home)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -20,10 +20,7 @@ defmodule PinchflatWeb.Router do
|
||||
get "/", PageController, :home
|
||||
|
||||
resources "/media_profiles", MediaProfiles.MediaProfileController
|
||||
|
||||
scope "/media_sources", MediaSources do
|
||||
resources "/sources", SourceController
|
||||
end
|
||||
resources "/sources", MediaSources.SourceController
|
||||
end
|
||||
|
||||
# Other scopes may use custom stacks.
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -13,14 +13,14 @@ defmodule PinchflatWeb.MediaProfileControllerTest do
|
||||
describe "index" do
|
||||
test "lists all media_profiles", %{conn: conn} do
|
||||
conn = get(conn, ~p"/media_profiles")
|
||||
assert html_response(conn, 200) =~ "Listing Media profiles"
|
||||
assert html_response(conn, 200) =~ "All Media Profiles"
|
||||
end
|
||||
end
|
||||
|
||||
describe "new media_profile" do
|
||||
test "renders form", %{conn: conn} do
|
||||
conn = get(conn, ~p"/media_profiles/new")
|
||||
assert html_response(conn, 200) =~ "New Media profile"
|
||||
assert html_response(conn, 200) =~ "New Media Profile"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -32,12 +32,12 @@ defmodule PinchflatWeb.MediaProfileControllerTest do
|
||||
assert redirected_to(conn) == ~p"/media_profiles/#{id}"
|
||||
|
||||
conn = get(conn, ~p"/media_profiles/#{id}")
|
||||
assert html_response(conn, 200) =~ "Media profile #{id}"
|
||||
assert html_response(conn, 200) =~ "Media Profile ##{id}"
|
||||
end
|
||||
|
||||
test "renders errors when data is invalid", %{conn: conn} do
|
||||
conn = post(conn, ~p"/media_profiles", media_profile: @invalid_attrs)
|
||||
assert html_response(conn, 200) =~ "New Media profile"
|
||||
assert html_response(conn, 200) =~ "New Media Profile"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -49,7 +49,7 @@ defmodule PinchflatWeb.MediaProfileControllerTest do
|
||||
media_profile: media_profile
|
||||
} do
|
||||
conn = get(conn, ~p"/media_profiles/#{media_profile}/edit")
|
||||
assert html_response(conn, 200) =~ "Edit Media profile"
|
||||
assert html_response(conn, 200) =~ "Edit Media Profile"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -66,7 +66,7 @@ defmodule PinchflatWeb.MediaProfileControllerTest do
|
||||
|
||||
test "renders errors when data is invalid", %{conn: conn, media_profile: media_profile} do
|
||||
conn = put(conn, ~p"/media_profiles/#{media_profile}", media_profile: @invalid_attrs)
|
||||
assert html_response(conn, 200) =~ "Edit Media profile"
|
||||
assert html_response(conn, 200) =~ "Edit Media Profile"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -28,14 +28,14 @@ defmodule PinchflatWeb.SourceControllerTest do
|
||||
|
||||
describe "index" do
|
||||
test "lists all sources", %{conn: conn} do
|
||||
conn = get(conn, ~p"/media_sources/sources")
|
||||
assert html_response(conn, 200) =~ "Listing Sources"
|
||||
conn = get(conn, ~p"/sources")
|
||||
assert html_response(conn, 200) =~ "All Sources"
|
||||
end
|
||||
end
|
||||
|
||||
describe "new source" do
|
||||
test "renders form", %{conn: conn} do
|
||||
conn = get(conn, ~p"/media_sources/sources/new")
|
||||
conn = get(conn, ~p"/sources/new")
|
||||
assert html_response(conn, 200) =~ "New Source"
|
||||
end
|
||||
end
|
||||
@@ -43,17 +43,17 @@ defmodule PinchflatWeb.SourceControllerTest do
|
||||
describe "create source" do
|
||||
test "redirects to show when data is valid", %{conn: conn, create_attrs: create_attrs} do
|
||||
expect(YtDlpRunnerMock, :run, 1, &runner_function_mock/3)
|
||||
conn = post(conn, ~p"/media_sources/sources", source: create_attrs)
|
||||
conn = post(conn, ~p"/sources", source: create_attrs)
|
||||
|
||||
assert %{id: id} = redirected_params(conn)
|
||||
assert redirected_to(conn) == ~p"/media_sources/sources/#{id}"
|
||||
assert redirected_to(conn) == ~p"/sources/#{id}"
|
||||
|
||||
conn = get(conn, ~p"/media_sources/sources/#{id}")
|
||||
assert html_response(conn, 200) =~ "Source #{id}"
|
||||
conn = get(conn, ~p"/sources/#{id}")
|
||||
assert html_response(conn, 200) =~ "Source ##{id}"
|
||||
end
|
||||
|
||||
test "renders errors when data is invalid", %{conn: conn, invalid_attrs: invalid_attrs} do
|
||||
conn = post(conn, ~p"/media_sources/sources", source: invalid_attrs)
|
||||
conn = post(conn, ~p"/sources", source: invalid_attrs)
|
||||
assert html_response(conn, 200) =~ "New Source"
|
||||
end
|
||||
end
|
||||
@@ -62,7 +62,7 @@ defmodule PinchflatWeb.SourceControllerTest do
|
||||
setup [:create_source]
|
||||
|
||||
test "renders form for editing chosen source", %{conn: conn, source: source} do
|
||||
conn = get(conn, ~p"/media_sources/sources/#{source}/edit")
|
||||
conn = get(conn, ~p"/sources/#{source}/edit")
|
||||
assert html_response(conn, 200) =~ "Edit Source"
|
||||
end
|
||||
end
|
||||
@@ -73,10 +73,10 @@ defmodule PinchflatWeb.SourceControllerTest do
|
||||
test "redirects when data is valid", %{conn: conn, source: source, update_attrs: update_attrs} do
|
||||
expect(YtDlpRunnerMock, :run, 1, &runner_function_mock/3)
|
||||
|
||||
conn = put(conn, ~p"/media_sources/sources/#{source}", source: update_attrs)
|
||||
assert redirected_to(conn) == ~p"/media_sources/sources/#{source}"
|
||||
conn = put(conn, ~p"/sources/#{source}", source: update_attrs)
|
||||
assert redirected_to(conn) == ~p"/sources/#{source}"
|
||||
|
||||
conn = get(conn, ~p"/media_sources/sources/#{source}")
|
||||
conn = get(conn, ~p"/sources/#{source}")
|
||||
assert html_response(conn, 200) =~ "https://www.youtube.com/source/321xyz"
|
||||
end
|
||||
|
||||
@@ -85,7 +85,7 @@ defmodule PinchflatWeb.SourceControllerTest do
|
||||
source: source,
|
||||
invalid_attrs: invalid_attrs
|
||||
} do
|
||||
conn = put(conn, ~p"/media_sources/sources/#{source}", source: invalid_attrs)
|
||||
conn = put(conn, ~p"/sources/#{source}", source: invalid_attrs)
|
||||
assert html_response(conn, 200) =~ "Edit Source"
|
||||
end
|
||||
end
|
||||
@@ -94,11 +94,11 @@ defmodule PinchflatWeb.SourceControllerTest do
|
||||
setup [:create_source]
|
||||
|
||||
test "deletes chosen source", %{conn: conn, source: source} do
|
||||
conn = delete(conn, ~p"/media_sources/sources/#{source}")
|
||||
assert redirected_to(conn) == ~p"/media_sources/sources"
|
||||
conn = delete(conn, ~p"/sources/#{source}")
|
||||
assert redirected_to(conn) == ~p"/sources"
|
||||
|
||||
assert_error_sent 404, fn ->
|
||||
get(conn, ~p"/media_sources/sources/#{source}")
|
||||
get(conn, ~p"/sources/#{source}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user