브랜드 에셋 사운드 스튜디오UI 수정
parent
cd4114f74d
commit
b05c7bd080
570
index.css
570
index.css
|
|
@ -511,10 +511,24 @@
|
||||||
|
|
||||||
/* Bottom Button Container */
|
/* Bottom Button Container */
|
||||||
.bottom-button-container {
|
.bottom-button-container {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 32px;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
padding: 1.5rem;
|
z-index: 30;
|
||||||
flex-shrink: 0;
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottom-button-container > * {
|
||||||
|
pointer-events: all;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.bottom-button-container {
|
||||||
|
left: 240px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* =====================================================
|
/* =====================================================
|
||||||
|
|
@ -7336,13 +7350,45 @@
|
||||||
Sound Studio Styles
|
Sound Studio Styles
|
||||||
===================================================== */
|
===================================================== */
|
||||||
|
|
||||||
|
/* Sound Studio Page */
|
||||||
|
.sound-studio-page {
|
||||||
|
height: 100%;
|
||||||
|
background-color: #002224;
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
padding: 80px 1rem 120px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.sound-studio-page {
|
||||||
|
padding: 80px 2rem 120px;
|
||||||
|
left: 240px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Sound Studio Header */
|
/* Sound Studio Header */
|
||||||
.sound-studio-header {
|
.sound-studio-header {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 64px;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 0.5rem 2rem;
|
padding: 0 1rem;
|
||||||
gap: 1rem;
|
z-index: 30;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sound-studio-header > * {
|
||||||
|
pointer-events: all;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.sound-studio-header {
|
||||||
|
left: 240px;
|
||||||
|
padding: 0 2rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-back-new {
|
.btn-back-new {
|
||||||
|
|
@ -7426,28 +7472,35 @@
|
||||||
.sound-studio-title {
|
.sound-studio-title {
|
||||||
font-size: 2rem;
|
font-size: 2rem;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: #E5F1F2;
|
color: #ffffff;
|
||||||
margin: 1rem auto 1.5rem auto;
|
text-align: center;
|
||||||
padding: 0 2rem;
|
|
||||||
max-width: 1200px;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
margin: 0 auto 1rem;
|
||||||
|
padding: 0;
|
||||||
line-height: 1.19;
|
line-height: 1.19;
|
||||||
letter-spacing: -0.006em;
|
letter-spacing: -0.006em;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Sound Studio Container */
|
/* Sound Studio Container */
|
||||||
.sound-studio-container {
|
.sound-studio-container {
|
||||||
|
height: 96%;
|
||||||
|
min-height: 600px;
|
||||||
background-color: #01393B;
|
background-color: #01393B;
|
||||||
border-radius: 40px;
|
border-radius: 24px;
|
||||||
padding: 2rem;
|
padding: 1.25rem 1rem;
|
||||||
margin: 0 auto 1.5rem auto;
|
margin: 0 auto;
|
||||||
max-width: 1136px;
|
max-width: 1440px;
|
||||||
width: calc(100% - 4rem);
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 1.25rem;
|
gap: 1.25rem;
|
||||||
flex: 1;
|
}
|
||||||
min-height: 0;
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.sound-studio-container {
|
||||||
|
border-radius: 40px;
|
||||||
|
padding: 2rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Sound Studio Columns */
|
/* Sound Studio Columns */
|
||||||
|
|
@ -7466,10 +7519,15 @@
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
gap: 1.25rem;
|
gap: 1.25rem;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sound-column > *:not(.btn-generate-sound):not(.error-message-new):not(.status-message-new) {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
/* Lyrics Column */
|
/* Lyrics Column */
|
||||||
.lyrics-column {
|
.lyrics-column {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
@ -7794,12 +7852,12 @@
|
||||||
background-color: #002224;
|
background-color: #002224;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
min-height: 200px;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.lyrics-textarea {
|
.lyrics-textarea {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
height: 200px;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
border: none;
|
border: none;
|
||||||
color: var(--color-text-white);
|
color: var(--color-text-white);
|
||||||
|
|
@ -7825,12 +7883,13 @@
|
||||||
|
|
||||||
/* Generate Sound Button */
|
/* Generate Sound Button */
|
||||||
.btn-generate-sound {
|
.btn-generate-sound {
|
||||||
width: 100%;
|
height: 48px;
|
||||||
padding: 0.625rem 2.5rem;
|
min-width: 120px;
|
||||||
background-color: #01393B;
|
padding: 0.625rem 1.25rem;
|
||||||
border: 1px solid var(--color-mint);
|
background-color: #94FBE0;
|
||||||
|
border: none;
|
||||||
border-radius: var(--radius-full);
|
border-radius: var(--radius-full);
|
||||||
color: var(--color-mint);
|
color: #000000;
|
||||||
font-size: var(--text-sm);
|
font-size: var(--text-sm);
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
@ -7844,8 +7903,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-generate-sound:hover:not(.disabled) {
|
.btn-generate-sound:hover:not(.disabled) {
|
||||||
background-color: var(--color-mint);
|
background-color: #6ef5ca;
|
||||||
color: #000000;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-generate-sound.disabled {
|
.btn-generate-sound.disabled {
|
||||||
|
|
@ -7942,27 +8000,10 @@
|
||||||
|
|
||||||
/* Responsive Styles for Sound Studio */
|
/* Responsive Styles for Sound Studio */
|
||||||
@media (max-width: 639px) {
|
@media (max-width: 639px) {
|
||||||
.sound-studio-header {
|
|
||||||
padding: 0.5rem 1rem;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: stretch;
|
|
||||||
}
|
|
||||||
|
|
||||||
.progress-indicator {
|
.progress-indicator {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sound-studio-title {
|
|
||||||
font-size: 1.5rem;
|
|
||||||
padding: 0 1rem;
|
|
||||||
margin: 0.75rem auto 1rem auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sound-studio-container {
|
|
||||||
padding: 1.25rem;
|
|
||||||
width: calc(100% - 2rem);
|
|
||||||
}
|
|
||||||
|
|
||||||
.sound-type-grid {
|
.sound-type-grid {
|
||||||
gap: 0.5rem;
|
gap: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
@ -7980,28 +8021,9 @@
|
||||||
padding: 0.5rem 0.75rem;
|
padding: 0.5rem 0.75rem;
|
||||||
font-size: var(--text-xs);
|
font-size: var(--text-xs);
|
||||||
}
|
}
|
||||||
|
|
||||||
.lyrics-display {
|
|
||||||
min-height: 150px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 640px) and (max-width: 767px) {
|
@media (min-width: 640px) and (max-width: 767px) {
|
||||||
.sound-studio-header {
|
|
||||||
padding: 0.5rem 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sound-studio-title {
|
|
||||||
font-size: 1.75rem;
|
|
||||||
padding: 0 1.5rem;
|
|
||||||
margin: 0.875rem auto 1.25rem auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sound-studio-container {
|
|
||||||
padding: 1.5rem;
|
|
||||||
width: calc(100% - 3rem);
|
|
||||||
}
|
|
||||||
|
|
||||||
.sound-studio-columns {
|
.sound-studio-columns {
|
||||||
gap: 2rem;
|
gap: 2rem;
|
||||||
}
|
}
|
||||||
|
|
@ -8014,21 +8036,15 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 1024px) {
|
@media (min-width: 1024px) {
|
||||||
.sound-studio-container {
|
|
||||||
padding: 2.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sound-studio-columns {
|
.sound-studio-columns {
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
gap: 3rem;
|
gap: 3rem;
|
||||||
overflow: hidden;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.sound-column {
|
.sound-column {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
width: auto;
|
width: auto;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
overflow-y: auto;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.lyrics-column {
|
.lyrics-column {
|
||||||
|
|
@ -8046,81 +8062,113 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Height-based responsive adjustments */
|
|
||||||
@media (max-height: 800px) {
|
|
||||||
.sound-studio-container {
|
|
||||||
gap: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sound-studio-columns {
|
|
||||||
gap: 2rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-height: 700px) {
|
|
||||||
.sound-studio-title {
|
|
||||||
font-size: 1.75rem;
|
|
||||||
margin: 0.75rem 2rem 1rem 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sound-studio-container {
|
|
||||||
padding: 1.5rem;
|
|
||||||
gap: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sound-studio-columns {
|
|
||||||
gap: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sound-studio-section {
|
|
||||||
gap: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.lyrics-display {
|
|
||||||
min-height: 180px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-height: 600px) {
|
|
||||||
.sound-studio-header {
|
|
||||||
padding: 0.375rem 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sound-studio-title {
|
|
||||||
font-size: 1.5rem;
|
|
||||||
margin: 0.5rem 2rem 0.75rem 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sound-studio-container {
|
|
||||||
padding: 1.25rem;
|
|
||||||
gap: 0.875rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sound-studio-columns {
|
|
||||||
gap: 1.25rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sound-type-btn,
|
|
||||||
.genre-btn {
|
|
||||||
padding: 0.625rem 0.75rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.lyrics-display {
|
|
||||||
min-height: 150px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ====================================
|
/* ====================================
|
||||||
Asset Management (Brand Asset) Styles
|
Asset Management (Brand Asset) Styles
|
||||||
==================================== */
|
==================================== */
|
||||||
|
|
||||||
/* Asset Header */
|
/* Asset Page Layout */
|
||||||
.asset-header {
|
.asset-page {
|
||||||
|
height: 100%;
|
||||||
|
background-color: #002224;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fixed Header - 뒤로가기 */
|
||||||
|
.asset-sticky-header {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 64px;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 0.5rem 2rem;
|
padding: 0 1rem;
|
||||||
margin-bottom: 1rem;
|
z-index: 30;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.asset-sticky-header > * {
|
||||||
|
pointer-events: all;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.asset-sticky-header {
|
||||||
|
left: 240px;
|
||||||
|
padding: 0 2rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 뒤로가기 버튼 */
|
||||||
|
.asset-back-btn {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
background-color: #462E64;
|
||||||
|
color: #CFABFB;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: 600;
|
||||||
|
padding: 0 1.25rem 0 0.5rem;
|
||||||
|
height: 36px;
|
||||||
|
border: 1px solid #694596;
|
||||||
|
border-radius: 999px;
|
||||||
|
cursor: pointer;
|
||||||
|
white-space: nowrap;
|
||||||
|
transition: background-color 0.2s;
|
||||||
|
line-height: 1.19;
|
||||||
|
letter-spacing: -0.006em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.asset-back-btn:hover {
|
||||||
|
background-color: #5a3a80;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Single Scroll Area */
|
||||||
|
.asset-scroll-area {
|
||||||
|
height: 100%;
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
padding: 80px 2rem 120px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.asset-scroll-area::-webkit-scrollbar {
|
||||||
|
width: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.asset-scroll-area::-webkit-scrollbar-track {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.asset-scroll-area::-webkit-scrollbar-thumb {
|
||||||
|
background: #379599;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.asset-scroll-area::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: #4AABAF;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fixed Footer - 다음 단계 */
|
||||||
|
.asset-sticky-footer {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 32px;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
z-index: 30;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.asset-sticky-footer > * {
|
||||||
|
pointer-events: all;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.asset-sticky-footer {
|
||||||
|
left: 240px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Asset Title */
|
/* Asset Title */
|
||||||
|
|
@ -8128,35 +8176,26 @@
|
||||||
font-size: 2rem;
|
font-size: 2rem;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: #E5F1F2;
|
color: #E5F1F2;
|
||||||
margin: 1rem auto 1.5rem auto;
|
padding: 0 0 1.5rem;
|
||||||
padding: 0 2rem;
|
text-align: center;
|
||||||
max-width: 1200px;
|
|
||||||
width: 100%;
|
|
||||||
line-height: 1.19;
|
line-height: 1.19;
|
||||||
letter-spacing: -0.006em;
|
letter-spacing: -0.006em;
|
||||||
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Asset Container */
|
/* Asset Container */
|
||||||
.asset-container {
|
.asset-container {
|
||||||
background-color: #01393B;
|
margin: 0 auto;
|
||||||
border-radius: 40px;
|
|
||||||
padding: 2rem;
|
|
||||||
margin: 0 auto 1.5rem auto;
|
|
||||||
max-width: 1136px;
|
max-width: 1136px;
|
||||||
width: calc(100% - 4rem);
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 1.5rem;
|
gap: 1rem;
|
||||||
flex: 1;
|
|
||||||
min-height: 0;
|
|
||||||
overflow-y: auto;
|
|
||||||
overflow-x: hidden;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 1024px) {
|
@media (min-width: 1024px) {
|
||||||
.asset-container {
|
.asset-container {
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
overflow: hidden;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -8164,18 +8203,24 @@
|
||||||
.asset-column {
|
.asset-column {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 1.25rem;
|
gap: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.asset-column-left {
|
.asset-column-left {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
|
background-color: #01393B;
|
||||||
|
border-radius: 24px;
|
||||||
|
padding: 1.25rem 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.asset-column-right {
|
.asset-column-right {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
|
gap: 1rem;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 1024px) {
|
@media (min-width: 1024px) {
|
||||||
|
|
@ -8183,6 +8228,8 @@
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
border-radius: 40px;
|
||||||
|
padding: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.asset-column-right {
|
.asset-column-right {
|
||||||
|
|
@ -8190,50 +8237,91 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Asset Section Header */
|
||||||
|
.asset-section-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 0.5rem;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.asset-section-header-left {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.asset-section-subtitle {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: 400;
|
||||||
|
color: #6AB0B3;
|
||||||
|
line-height: 1.19;
|
||||||
|
letter-spacing: -0.006em;
|
||||||
|
}
|
||||||
|
|
||||||
/* Asset Section Title */
|
/* Asset Section Title */
|
||||||
.asset-section-title {
|
.asset-section-title {
|
||||||
font-size: 1.125rem;
|
font-size: 1.5rem;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: #94FBE0;
|
color: #94FBE0;
|
||||||
line-height: 1.19;
|
line-height: 1.19;
|
||||||
letter-spacing: -0.006em;
|
letter-spacing: -0.009em;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Mobile Upload Button (visible only on mobile/tablet) */
|
||||||
|
.asset-mobile-upload-btn {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
background-color: #94FBE0;
|
||||||
|
color: #000;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
font-weight: 600;
|
||||||
|
padding: 0 10px;
|
||||||
|
height: 24px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 999px;
|
||||||
|
cursor: pointer;
|
||||||
|
white-space: nowrap;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 1024px) {
|
||||||
|
.asset-mobile-upload-btn {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Asset Image List */
|
/* Asset Image List */
|
||||||
.asset-image-list {
|
.asset-image-list {
|
||||||
min-height: 120px;
|
|
||||||
max-height: 200px;
|
|
||||||
overflow-y: auto;
|
|
||||||
overscroll-behavior: contain;
|
|
||||||
background-color: #002224;
|
background-color: #002224;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
padding: 0.5rem;
|
padding: 0.5rem;
|
||||||
|
max-height: 1080px;
|
||||||
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 1024px) {
|
/* Load More Button */
|
||||||
.asset-image-list {
|
.asset-load-more {
|
||||||
flex: 1;
|
width: 100%;
|
||||||
min-height: 0;
|
padding: 0.625rem 1rem;
|
||||||
max-height: none;
|
background-color: #002224;
|
||||||
}
|
color: #9BCACC;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: 600;
|
||||||
|
border: 1px solid #379599;
|
||||||
|
border-radius: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.2s, border-color 0.2s;
|
||||||
|
line-height: 1.19;
|
||||||
|
letter-spacing: -0.006em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.asset-image-list::-webkit-scrollbar {
|
.asset-load-more:hover {
|
||||||
width: 6px;
|
background-color: #003A3C;
|
||||||
}
|
border-color: #4AABAF;
|
||||||
|
|
||||||
.asset-image-list::-webkit-scrollbar-track {
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
.asset-image-list::-webkit-scrollbar-thumb {
|
|
||||||
background: #379599;
|
|
||||||
border-radius: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.asset-image-list::-webkit-scrollbar-thumb:hover {
|
|
||||||
background: #4AABAF;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Asset Image Grid */
|
/* Asset Image Grid */
|
||||||
|
|
@ -8294,10 +8382,19 @@
|
||||||
|
|
||||||
/* Asset Upload Section */
|
/* Asset Upload Section */
|
||||||
.asset-upload-section {
|
.asset-upload-section {
|
||||||
display: flex;
|
display: none;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 1.25rem;
|
gap: 1rem;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
background-color: #01393B;
|
||||||
|
border-radius: 40px;
|
||||||
|
padding: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 1024px) {
|
||||||
|
.asset-upload-section {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.asset-upload-zone {
|
.asset-upload-zone {
|
||||||
|
|
@ -8332,20 +8429,31 @@
|
||||||
.asset-ratio-section {
|
.asset-ratio-section {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 1.25rem;
|
gap: 1rem;
|
||||||
|
background-color: #01393B;
|
||||||
|
border-radius: 24px;
|
||||||
|
padding: 1.25rem 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 1024px) {
|
||||||
|
.asset-ratio-section {
|
||||||
|
border-radius: 40px;
|
||||||
|
padding: 2rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.asset-ratio-buttons {
|
.asset-ratio-buttons {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 0.75rem;
|
gap: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.asset-ratio-button {
|
.asset-ratio-button {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
padding: 1rem;
|
padding: 0 1rem;
|
||||||
|
height: 48px;
|
||||||
background-color: #002224;
|
background-color: #002224;
|
||||||
border: 1px solid transparent;
|
border: 1px solid transparent;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
|
|
@ -8356,6 +8464,7 @@
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
line-height: 1.19;
|
line-height: 1.19;
|
||||||
letter-spacing: -0.006em;
|
letter-spacing: -0.006em;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.asset-ratio-button:hover {
|
.asset-ratio-button:hover {
|
||||||
|
|
@ -8367,6 +8476,17 @@
|
||||||
background-color: #002224;
|
background-color: #002224;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.asset-ratio-label {
|
||||||
|
font-weight: 600;
|
||||||
|
color: #E5F1F2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.asset-ratio-subtitle {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: 400;
|
||||||
|
color: #9BCACC;
|
||||||
|
}
|
||||||
|
|
||||||
.asset-ratio-icon {
|
.asset-ratio-icon {
|
||||||
width: 32px;
|
width: 32px;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
|
|
@ -8392,14 +8512,7 @@
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Asset Bottom Button */
|
/* Asset Next Button */
|
||||||
.asset-bottom {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.5rem;
|
|
||||||
padding: 0 2rem 1.5rem 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.asset-next-button {
|
.asset-next-button {
|
||||||
background-color: #AE72F9;
|
background-color: #AE72F9;
|
||||||
|
|
@ -8426,83 +8539,36 @@
|
||||||
|
|
||||||
/* Responsive Styles */
|
/* Responsive Styles */
|
||||||
@media (max-width: 639px) {
|
@media (max-width: 639px) {
|
||||||
.asset-header {
|
|
||||||
padding: 0.5rem 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.asset-title {
|
.asset-title {
|
||||||
font-size: 1.5rem;
|
font-size: 1.5rem;
|
||||||
padding: 0 1rem;
|
padding-bottom: 1rem;
|
||||||
margin: 0.75rem auto 1rem auto;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.asset-container {
|
.asset-scroll-area {
|
||||||
padding: 1.25rem;
|
padding: 80px 1rem 120px;
|
||||||
width: calc(100% - 2rem);
|
|
||||||
gap: 1.25rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.asset-image-grid {
|
.asset-image-grid {
|
||||||
grid-template-columns: repeat(2, 1fr);
|
grid-template-columns: repeat(2, 1fr);
|
||||||
}
|
}
|
||||||
|
|
||||||
.asset-bottom {
|
|
||||||
padding: 0 1rem 1rem 1rem;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 640px) and (max-width: 767px) {
|
@media (min-width: 640px) and (max-width: 767px) {
|
||||||
.asset-header {
|
|
||||||
padding: 0.5rem 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.asset-title {
|
.asset-title {
|
||||||
font-size: 1.75rem;
|
font-size: 1.75rem;
|
||||||
padding: 0 1.5rem;
|
padding-bottom: 1.25rem;
|
||||||
margin: 0.875rem auto 1.25rem auto;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.asset-container {
|
.asset-scroll-area {
|
||||||
padding: 1.5rem;
|
padding: 80px 1.5rem 120px;
|
||||||
width: calc(100% - 3rem);
|
|
||||||
}
|
|
||||||
|
|
||||||
.asset-bottom {
|
|
||||||
padding: 0 1.5rem 1.25rem 1.5rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 768px) and (max-width: 1023px) {
|
|
||||||
.asset-container {
|
|
||||||
padding: 1.75rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 1024px) {
|
|
||||||
.asset-container {
|
|
||||||
padding: 2.5rem;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Height-based responsive adjustments */
|
/* Height-based responsive adjustments */
|
||||||
@media (max-height: 800px) {
|
|
||||||
.asset-container {
|
|
||||||
padding: 1.75rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.asset-upload-zone {
|
|
||||||
min-height: 120px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-height: 700px) {
|
@media (max-height: 700px) {
|
||||||
.asset-container {
|
|
||||||
padding: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.asset-title {
|
.asset-title {
|
||||||
font-size: 1.75rem;
|
font-size: 1.75rem;
|
||||||
margin: 0.75rem auto 1rem auto;
|
padding-bottom: 0.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.asset-upload-zone {
|
.asset-upload-zone {
|
||||||
|
|
@ -8511,20 +8577,6 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-height: 600px) {
|
@media (max-height: 600px) {
|
||||||
.asset-container {
|
|
||||||
padding: 1.25rem;
|
|
||||||
gap: 0.875rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.asset-column {
|
|
||||||
gap: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.asset-upload-section,
|
|
||||||
.asset-ratio-section {
|
|
||||||
gap: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.asset-upload-zone {
|
.asset-upload-zone {
|
||||||
min-height: 80px;
|
min-height: 80px;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -129,6 +129,11 @@
|
||||||
"imageUpload": "Image Upload",
|
"imageUpload": "Image Upload",
|
||||||
"dragAndDrop": "Drag and drop\nimages to upload",
|
"dragAndDrop": "Drag and drop\nimages to upload",
|
||||||
"videoRatio": "Video Ratio",
|
"videoRatio": "Video Ratio",
|
||||||
|
"minImages": "Min. 5 images",
|
||||||
|
"youtubeShorts": "YouTube Shorts",
|
||||||
|
"youtubeVideo": "YouTube Video",
|
||||||
|
"back": "Go Back",
|
||||||
|
"loadMore": "Load more",
|
||||||
"uploadFailed": "Image upload failed.",
|
"uploadFailed": "Image upload failed.",
|
||||||
"uploading": "Uploading...",
|
"uploading": "Uploading...",
|
||||||
"nextStep": "Next Step"
|
"nextStep": "Next Step"
|
||||||
|
|
|
||||||
|
|
@ -129,6 +129,11 @@
|
||||||
"imageUpload": "이미지 업로드",
|
"imageUpload": "이미지 업로드",
|
||||||
"dragAndDrop": "이미지를 드래그하여\n업로드",
|
"dragAndDrop": "이미지를 드래그하여\n업로드",
|
||||||
"videoRatio": "영상 비율",
|
"videoRatio": "영상 비율",
|
||||||
|
"minImages": "최소 5장",
|
||||||
|
"youtubeShorts": "유튜브 쇼츠",
|
||||||
|
"youtubeVideo": "유튜브 일반",
|
||||||
|
"back": "뒤로가기",
|
||||||
|
"loadMore": "더보기",
|
||||||
"uploadFailed": "이미지 업로드에 실패했습니다.",
|
"uploadFailed": "이미지 업로드에 실패했습니다.",
|
||||||
"uploading": "업로드 중...",
|
"uploading": "업로드 중...",
|
||||||
"nextStep": "다음 단계"
|
"nextStep": "다음 단계"
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import { uploadImages } from '../../utils/api';
|
||||||
|
|
||||||
interface AssetManagementContentProps {
|
interface AssetManagementContentProps {
|
||||||
onNext: (imageTaskId: string) => void;
|
onNext: (imageTaskId: string) => void;
|
||||||
|
onBack?: () => void;
|
||||||
imageList: ImageItem[];
|
imageList: ImageItem[];
|
||||||
onRemoveImage: (index: number) => void;
|
onRemoveImage: (index: number) => void;
|
||||||
onAddImages: (files: File[]) => void;
|
onAddImages: (files: File[]) => void;
|
||||||
|
|
@ -13,20 +14,22 @@ interface AssetManagementContentProps {
|
||||||
|
|
||||||
type VideoRatio = 'vertical' | 'horizontal';
|
type VideoRatio = 'vertical' | 'horizontal';
|
||||||
|
|
||||||
|
const IMAGES_PER_PAGE = 12;
|
||||||
|
|
||||||
const AssetManagementContent: React.FC<AssetManagementContentProps> = ({
|
const AssetManagementContent: React.FC<AssetManagementContentProps> = ({
|
||||||
onNext,
|
onNext,
|
||||||
|
onBack,
|
||||||
imageList,
|
imageList,
|
||||||
onRemoveImage,
|
onRemoveImage,
|
||||||
onAddImages,
|
onAddImages,
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||||
const imageListRef = useRef<HTMLDivElement>(null);
|
|
||||||
const [isUploading, setIsUploading] = useState(false);
|
const [isUploading, setIsUploading] = useState(false);
|
||||||
const [uploadError, setUploadError] = useState<string | null>(null);
|
const [uploadError, setUploadError] = useState<string | null>(null);
|
||||||
const [videoRatio, setVideoRatio] = useState<VideoRatio>('vertical');
|
const [videoRatio, setVideoRatio] = useState<VideoRatio>('vertical');
|
||||||
|
const [displayCount, setDisplayCount] = useState(IMAGES_PER_PAGE);
|
||||||
|
|
||||||
// Load video ratio from localStorage on mount
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const savedRatio = localStorage.getItem('castad_video_ratio') as VideoRatio;
|
const savedRatio = localStorage.getItem('castad_video_ratio') as VideoRatio;
|
||||||
if (savedRatio === 'vertical' || savedRatio === 'horizontal') {
|
if (savedRatio === 'vertical' || savedRatio === 'horizontal') {
|
||||||
|
|
@ -34,7 +37,6 @@ const AssetManagementContent: React.FC<AssetManagementContentProps> = ({
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// Save video ratio to localStorage when it changes
|
|
||||||
const handleVideoRatioChange = (ratio: VideoRatio) => {
|
const handleVideoRatioChange = (ratio: VideoRatio) => {
|
||||||
setVideoRatio(ratio);
|
setVideoRatio(ratio);
|
||||||
localStorage.setItem('castad_video_ratio', ratio);
|
localStorage.setItem('castad_video_ratio', ratio);
|
||||||
|
|
@ -51,7 +53,6 @@ const AssetManagementContent: React.FC<AssetManagementContentProps> = ({
|
||||||
setUploadError(null);
|
setUploadError(null);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// URL 이미지와 파일 이미지 분리
|
|
||||||
const urlImages: ImageUrlItem[] = imageList
|
const urlImages: ImageUrlItem[] = imageList
|
||||||
.filter((item: ImageItem): item is ImageItem & { type: 'url' } => item.type === 'url')
|
.filter((item: ImageItem): item is ImageItem & { type: 'url' } => item.type === 'url')
|
||||||
.map((item) => ({ url: item.url }));
|
.map((item) => ({ url: item.url }));
|
||||||
|
|
@ -60,7 +61,6 @@ const AssetManagementContent: React.FC<AssetManagementContentProps> = ({
|
||||||
.filter((item: ImageItem): item is ImageItem & { type: 'file' } => item.type === 'file')
|
.filter((item: ImageItem): item is ImageItem & { type: 'file' } => item.type === 'file')
|
||||||
.map((item) => item.file);
|
.map((item) => item.file);
|
||||||
|
|
||||||
// 이미지가 하나라도 있으면 업로드
|
|
||||||
if (urlImages.length > 0 || fileImages.length > 0) {
|
if (urlImages.length > 0 || fileImages.length > 0) {
|
||||||
const response = await uploadImages(urlImages, fileImages);
|
const response = await uploadImages(urlImages, fileImages);
|
||||||
onNext(response.task_id);
|
onNext(response.task_id);
|
||||||
|
|
@ -73,11 +73,6 @@ const AssetManagementContent: React.FC<AssetManagementContentProps> = ({
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleImageListWheel = (e: React.WheelEvent) => {
|
|
||||||
// 이 영역 안에서는 항상 스크롤 이벤트 전파 차단
|
|
||||||
e.stopPropagation();
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleDragOver = (e: React.DragEvent) => {
|
const handleDragOver = (e: React.DragEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
@ -86,14 +81,10 @@ const AssetManagementContent: React.FC<AssetManagementContentProps> = ({
|
||||||
const handleDrop = (e: React.DragEvent) => {
|
const handleDrop = (e: React.DragEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
const files = Array.from(e.dataTransfer.files).filter((file: File) =>
|
||||||
const files = Array.from(e.dataTransfer.files).filter(file =>
|
|
||||||
file.type.startsWith('image/')
|
file.type.startsWith('image/')
|
||||||
);
|
);
|
||||||
|
if (files.length > 0) onAddImages(files);
|
||||||
if (files.length > 0) {
|
|
||||||
onAddImages(files);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleFileSelect = () => {
|
const handleFileSelect = () => {
|
||||||
|
|
@ -108,24 +99,48 @@ const AssetManagementContent: React.FC<AssetManagementContentProps> = ({
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const visibleImages = imageList.slice(0, displayCount);
|
||||||
|
const hasMore = imageList.length > displayCount;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main className="page-container">
|
<main className="asset-page">
|
||||||
{/* Title */}
|
{/* Fixed Header - 뒤로가기 버튼 */}
|
||||||
|
<div className="asset-sticky-header">
|
||||||
|
{onBack && (
|
||||||
|
<button onClick={onBack} className="asset-back-btn">
|
||||||
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
|
||||||
|
<polyline points="15 18 9 12 15 6"/>
|
||||||
|
</svg>
|
||||||
|
{t('assetManagement.back')}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Single Scrollable Content Area */}
|
||||||
|
<div className="asset-scroll-area">
|
||||||
<h1 className="asset-title">{t('assetManagement.title')}</h1>
|
<h1 className="asset-title">{t('assetManagement.title')}</h1>
|
||||||
|
|
||||||
{/* Main Content Container */}
|
|
||||||
<div className="asset-container">
|
<div className="asset-container">
|
||||||
{/* Left Column - Selected Images */}
|
{/* Left Column - Selected Images */}
|
||||||
<div className="asset-column asset-column-left">
|
<div className="asset-column asset-column-left">
|
||||||
|
<div className="asset-section-header">
|
||||||
|
<div className="asset-section-header-left">
|
||||||
<h3 className="asset-section-title">{t('assetManagement.selectedImages')}</h3>
|
<h3 className="asset-section-title">{t('assetManagement.selectedImages')}</h3>
|
||||||
<div
|
<span className="asset-section-subtitle">{t('assetManagement.minImages')}</span>
|
||||||
ref={imageListRef}
|
</div>
|
||||||
onWheel={handleImageListWheel}
|
<button onClick={handleFileSelect} className="asset-mobile-upload-btn">
|
||||||
className="asset-image-list"
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none">
|
||||||
>
|
<line x1="8" y1="2" x2="8" y2="14" stroke="currentColor" strokeWidth="2" strokeLinecap="round"/>
|
||||||
{imageList.length > 0 ? (
|
<line x1="2" y1="8" x2="14" y2="8" stroke="currentColor" strokeWidth="2" strokeLinecap="round"/>
|
||||||
|
</svg>
|
||||||
|
{t('assetManagement.imageUpload')}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="asset-image-list">
|
||||||
|
{visibleImages.length > 0 && (
|
||||||
<div className="asset-image-grid">
|
<div className="asset-image-grid">
|
||||||
{imageList.map((item, i) => (
|
{visibleImages.map((item, i) => (
|
||||||
<div key={i} className="asset-image-item">
|
<div key={i} className="asset-image-item">
|
||||||
<img
|
<img
|
||||||
src={getImageSrc(item)}
|
src={getImageSrc(item)}
|
||||||
|
|
@ -146,13 +161,22 @@ const AssetManagementContent: React.FC<AssetManagementContentProps> = ({
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{hasMore && (
|
||||||
|
<button
|
||||||
|
className="asset-load-more"
|
||||||
|
onClick={() => setDisplayCount(prev => prev + IMAGES_PER_PAGE)}
|
||||||
|
>
|
||||||
|
{t('assetManagement.loadMore')}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Right Column - Upload and Video Ratio */}
|
{/* Right Column - Upload and Video Ratio */}
|
||||||
<div className="asset-column asset-column-right">
|
<div className="asset-column asset-column-right">
|
||||||
{/* Image Upload Section */}
|
{/* Image Upload Section (desktop only) */}
|
||||||
<div className="asset-upload-section">
|
<div className="asset-upload-section">
|
||||||
<h3 className="asset-section-title">{t('assetManagement.imageUpload')}</h3>
|
<h3 className="asset-section-title">{t('assetManagement.imageUpload')}</h3>
|
||||||
<div
|
<div
|
||||||
|
|
@ -167,14 +191,6 @@ const AssetManagementContent: React.FC<AssetManagementContentProps> = ({
|
||||||
))}
|
))}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<input
|
|
||||||
ref={fileInputRef}
|
|
||||||
type="file"
|
|
||||||
accept="image/*"
|
|
||||||
multiple
|
|
||||||
onChange={handleFileChange}
|
|
||||||
className="hidden"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Video Ratio Section */}
|
{/* Video Ratio Section */}
|
||||||
|
|
@ -188,7 +204,8 @@ const AssetManagementContent: React.FC<AssetManagementContentProps> = ({
|
||||||
<div className="asset-ratio-icon asset-ratio-icon-vertical">
|
<div className="asset-ratio-icon asset-ratio-icon-vertical">
|
||||||
<div className="asset-ratio-box"></div>
|
<div className="asset-ratio-box"></div>
|
||||||
</div>
|
</div>
|
||||||
<span>9:16</span>
|
<span className="asset-ratio-label">9:16</span>
|
||||||
|
<span className="asset-ratio-subtitle">{t('assetManagement.youtubeShorts')}</span>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => handleVideoRatioChange('horizontal')}
|
onClick={() => handleVideoRatioChange('horizontal')}
|
||||||
|
|
@ -197,15 +214,17 @@ const AssetManagementContent: React.FC<AssetManagementContentProps> = ({
|
||||||
<div className="asset-ratio-icon asset-ratio-icon-horizontal">
|
<div className="asset-ratio-icon asset-ratio-icon-horizontal">
|
||||||
<div className="asset-ratio-box"></div>
|
<div className="asset-ratio-box"></div>
|
||||||
</div>
|
</div>
|
||||||
<span>16:9</span>
|
<span className="asset-ratio-label">16:9</span>
|
||||||
|
<span className="asset-ratio-subtitle">{t('assetManagement.youtubeVideo')}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Bottom Button */}
|
{/* Fixed Footer - 다음 단계 버튼 */}
|
||||||
<div className="asset-bottom">
|
<div className="asset-sticky-footer">
|
||||||
{uploadError && (
|
{uploadError && (
|
||||||
<p className="text-red-500 text-sm mb-2">{uploadError}</p>
|
<p className="text-red-500 text-sm mb-2">{uploadError}</p>
|
||||||
)}
|
)}
|
||||||
|
|
@ -217,6 +236,15 @@ const AssetManagementContent: React.FC<AssetManagementContentProps> = ({
|
||||||
{isUploading ? t('assetManagement.uploading') : t('assetManagement.nextStep')}
|
{isUploading ? t('assetManagement.uploading') : t('assetManagement.nextStep')}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<input
|
||||||
|
ref={fileInputRef}
|
||||||
|
type="file"
|
||||||
|
accept="image/*"
|
||||||
|
multiple
|
||||||
|
onChange={handleFileChange}
|
||||||
|
className="hidden"
|
||||||
|
/>
|
||||||
</main>
|
</main>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -363,6 +363,7 @@ const GenerationFlow: React.FC<GenerationFlowProps> = ({
|
||||||
case 1:
|
case 1:
|
||||||
return (
|
return (
|
||||||
<AssetManagementContent
|
<AssetManagementContent
|
||||||
|
onBack={() => goToWizardStep(0)}
|
||||||
onNext={(taskId: string) => {
|
onNext={(taskId: string) => {
|
||||||
// Clear video generation state to start fresh
|
// Clear video generation state to start fresh
|
||||||
localStorage.removeItem(VIDEO_GENERATION_KEY);
|
localStorage.removeItem(VIDEO_GENERATION_KEY);
|
||||||
|
|
|
||||||
|
|
@ -386,7 +386,7 @@ const SoundStudioContent: React.FC<SoundStudioContentProps> = ({
|
||||||
const isGenerating = status === 'generating_lyric' || status === 'generating_song' || status === 'polling';
|
const isGenerating = status === 'generating_lyric' || status === 'generating_song' || status === 'polling';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main className="page-container">
|
<main className="sound-studio-page">
|
||||||
{audioUrl && (
|
{audioUrl && (
|
||||||
<audio ref={audioRef} src={audioUrl} preload="metadata" />
|
<audio ref={audioRef} src={audioUrl} preload="metadata" />
|
||||||
)}
|
)}
|
||||||
|
|
@ -517,6 +517,41 @@ const SoundStudioContent: React.FC<SoundStudioContentProps> = ({
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Generate Button */}
|
||||||
|
<button
|
||||||
|
onClick={handleGenerateMusic}
|
||||||
|
disabled={isGenerating}
|
||||||
|
className={`btn-generate-sound ${isGenerating ? 'disabled' : ''}`}
|
||||||
|
>
|
||||||
|
{isGenerating ? (
|
||||||
|
<>
|
||||||
|
<svg className="animate-spin h-4 w-4" viewBox="0 0 24 24">
|
||||||
|
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" fill="none"/>
|
||||||
|
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"/>
|
||||||
|
</svg>
|
||||||
|
{t('soundStudio.generating')}
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
t('soundStudio.generateButton')
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{errorMessage && (
|
||||||
|
<div className="error-message-new">
|
||||||
|
{errorMessage}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{isGenerating && statusMessage && (
|
||||||
|
<div className="status-message-new">
|
||||||
|
<svg className="animate-spin h-4 w-4" viewBox="0 0 24 24">
|
||||||
|
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" fill="none"/>
|
||||||
|
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"/>
|
||||||
|
</svg>
|
||||||
|
{statusMessage}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Right Column - Lyrics */}
|
{/* Right Column - Lyrics */}
|
||||||
|
|
@ -578,41 +613,6 @@ const SoundStudioContent: React.FC<SoundStudioContentProps> = ({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Generate Button */}
|
|
||||||
<button
|
|
||||||
onClick={handleGenerateMusic}
|
|
||||||
disabled={isGenerating}
|
|
||||||
className={`btn-generate-sound ${isGenerating ? 'disabled' : ''}`}
|
|
||||||
>
|
|
||||||
{isGenerating ? (
|
|
||||||
<>
|
|
||||||
<svg className="animate-spin h-4 w-4" viewBox="0 0 24 24">
|
|
||||||
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" fill="none"/>
|
|
||||||
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"/>
|
|
||||||
</svg>
|
|
||||||
{t('soundStudio.generating')}
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
t('soundStudio.generateButton')
|
|
||||||
)}
|
|
||||||
</button>
|
|
||||||
|
|
||||||
{errorMessage && (
|
|
||||||
<div className="error-message-new">
|
|
||||||
{errorMessage}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{isGenerating && statusMessage && (
|
|
||||||
<div className="status-message-new">
|
|
||||||
<svg className="animate-spin h-4 w-4" viewBox="0 0 24 24">
|
|
||||||
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" fill="none"/>
|
|
||||||
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"/>
|
|
||||||
</svg>
|
|
||||||
{statusMessage}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Bottom Button */}
|
{/* Bottom Button */}
|
||||||
|
|
@ -653,3 +653,4 @@ const SoundStudioContent: React.FC<SoundStudioContentProps> = ({
|
||||||
};
|
};
|
||||||
|
|
||||||
export default SoundStudioContent;
|
export default SoundStudioContent;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue