Redesign movie and show search pages with new UI
Major UI/UX overhaul for SearchMovies.razor and SearchShows.razor, including new navigation toggles, animated search forms, improved results display, and enhanced styling. Moved layout and header styles from MainLayout.razor to CSS, unified button styles on Home.razor, and added interactive elements and animations for a more engaging user experience.
This commit is contained in:
@@ -28,42 +28,4 @@
|
|||||||
height: 100vh;
|
height: 100vh;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar {
|
|
||||||
width: 250px;
|
|
||||||
background: #ffffff;
|
|
||||||
border-right: 1px solid rgba(0, 0, 0, 0.06);
|
|
||||||
position: relative;
|
|
||||||
z-index: 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
.main-content {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
min-height: 0;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.top-row {
|
|
||||||
background: rgba(255, 255, 255, 0.95);
|
|
||||||
border-bottom: 1px solid rgba(0, 0, 0, 0.06);
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end;
|
|
||||||
align-items: center;
|
|
||||||
height: 3.5rem;
|
|
||||||
padding: 0 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.top-row a {
|
|
||||||
color: #6c757d;
|
|
||||||
text-decoration: none;
|
|
||||||
font-weight: 500;
|
|
||||||
font-size: 14px;
|
|
||||||
transition: color 0.2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.top-row a:hover {
|
|
||||||
color: #667eea;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -8,34 +8,6 @@ main {
|
|||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar {
|
|
||||||
background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.top-row {
|
|
||||||
background-color: #f7f7f7;
|
|
||||||
border-bottom: 1px solid #d6d5d5;
|
|
||||||
justify-content: flex-end;
|
|
||||||
height: 3.5rem;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.top-row ::deep a, .top-row ::deep .btn-link {
|
|
||||||
white-space: nowrap;
|
|
||||||
margin-left: 1.5rem;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.top-row ::deep a:hover, .top-row ::deep .btn-link:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.top-row ::deep a:first-child {
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 640.98px) {
|
@media (max-width: 640.98px) {
|
||||||
.top-row {
|
.top-row {
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
<p class="description">This app helps you quickly identify actors, find out what else they've been in, and explore cast details for your favorite <strong>movies</strong> and <strong>TV shows</strong>.</p>
|
<p class="description">This app helps you quickly identify actors, find out what else they've been in, and explore cast details for your favorite <strong>movies</strong> and <strong>TV shows</strong>.</p>
|
||||||
<div class="cta-buttons">
|
<div class="cta-buttons">
|
||||||
<NavLink class="cta-button primary" href="/searchmovies">🎬 Search Movies</NavLink>
|
<NavLink class="cta-button primary" href="/searchmovies">🎬 Search Movies</NavLink>
|
||||||
<NavLink class="cta-button secondary" href="/searchshows">📺 Search TV Shows</NavLink>
|
<NavLink class="cta-button primary" href="/searchshows">📺 Search TV Shows</NavLink>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -180,24 +180,13 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.cta-button.primary {
|
.cta-button.primary {
|
||||||
background: linear-gradient(45deg, #667eea, #764ba2);
|
|
||||||
color: white;
|
|
||||||
box-shadow: 0 10px 30px rgba(102, 126, 234, 0.4);
|
|
||||||
}
|
|
||||||
|
|
||||||
.cta-button.primary:hover {
|
|
||||||
transform: translateY(-3px) scale(1.05);
|
|
||||||
box-shadow: 0 20px 40px rgba(102, 126, 234, 0.6);
|
|
||||||
}
|
|
||||||
|
|
||||||
.cta-button.secondary {
|
|
||||||
background: rgba(255, 255, 255, 0.15);
|
background: rgba(255, 255, 255, 0.15);
|
||||||
color: white;
|
color: white;
|
||||||
border: 2px solid rgba(255, 255, 255, 0.3);
|
border: 2px solid rgba(255, 255, 255, 0.3);
|
||||||
backdrop-filter: blur(10px);
|
backdrop-filter: blur(10px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.cta-button.secondary:hover {
|
.cta-button.primary:hover {
|
||||||
transform: translateY(-3px) scale(1.05);
|
transform: translateY(-3px) scale(1.05);
|
||||||
background: rgba(255, 255, 255, 0.25);
|
background: rgba(255, 255, 255, 0.25);
|
||||||
border-color: rgba(255, 255, 255, 0.5);
|
border-color: rgba(255, 255, 255, 0.5);
|
||||||
|
|||||||
@@ -3,100 +3,886 @@
|
|||||||
@using MovieActorLookup.Models.Movies
|
@using MovieActorLookup.Models.Movies
|
||||||
@using System.ComponentModel.DataAnnotations
|
@using System.ComponentModel.DataAnnotations
|
||||||
@inject MovieService MovieService
|
@inject MovieService MovieService
|
||||||
|
@inject IJSRuntime JSRuntime
|
||||||
@rendermode InteractiveServer
|
@rendermode InteractiveServer
|
||||||
|
|
||||||
<h3>Search for Actor by Movie and Character Name</h3>
|
<div class="search-container">
|
||||||
|
<div class="search-header">
|
||||||
|
<div class="navigation-toggle">
|
||||||
|
<a href="/searchshows" class="nav-btn shows-btn">
|
||||||
|
<span class="nav-icon">📺</span>
|
||||||
|
<span class="nav-text">Switch to TV Shows</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<h2 class="search-title">Discover Actors & Their Movies</h2>
|
||||||
|
<p class="search-subtitle">Search by movie and character to explore an actor's complete filmography</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="search-form">
|
||||||
<input type="text" class="form-control" @bind="_movieTitle" placeholder="Enter movie title..." />
|
<div class="input-group">
|
||||||
<input type="text" class="form-control" @bind="_characterName" placeholder="Enter character name..." />
|
<div class="input-wrapper">
|
||||||
<button class="btn btn-primary mt-2" @onclick="SearchForActorAndCharacters">Search</button>
|
<input type="text"
|
||||||
</div>
|
class="search-input"
|
||||||
|
@bind="_movieTitle"
|
||||||
|
placeholder="Enter movie title..."
|
||||||
|
@onkeypress="@(async (e) => { if (e.Key == "Enter") await SearchForActorAndCharacters(); })" />
|
||||||
|
<div class="input-icon">🎬</div>
|
||||||
|
</div>
|
||||||
|
<div class="input-wrapper">
|
||||||
|
<input type="text"
|
||||||
|
class="search-input"
|
||||||
|
@bind="_characterName"
|
||||||
|
placeholder="Enter character name..."
|
||||||
|
@onkeypress="@(async (e) => { if (e.Key == "Enter") await SearchForActorAndCharacters(); })" />
|
||||||
|
<div class="input-icon">🎭</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button class="search-btn @(_isSearching ? "searching" : "")"
|
||||||
|
@onclick="SearchForActorAndCharacters"
|
||||||
|
disabled="@_isSearching">
|
||||||
|
@if (_isSearching)
|
||||||
|
{
|
||||||
|
<span class="spinner"></span>
|
||||||
|
<span>Searching...</span>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<span>🔍 Search</span>
|
||||||
|
}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
@if (_isSearching)
|
@if (_isSearching)
|
||||||
{
|
{
|
||||||
<p>Searching...</p>
|
<div class="loading-state">
|
||||||
}
|
<div class="loading-animation">
|
||||||
|
<div class="loading-dot"></div>
|
||||||
else if (_actor != null)
|
<div class="loading-dot"></div>
|
||||||
{
|
<div class="loading-dot"></div>
|
||||||
<h4>Actor: @_actor.Name</h4>
|
</div>
|
||||||
|
<p>Finding the perfect match...</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
else if (_actor != null)
|
||||||
|
{
|
||||||
|
<div class="results-section">
|
||||||
|
<div class="actor-header">
|
||||||
|
<h3 class="actor-name">@_actor.Name</h3>
|
||||||
|
<div class="results-count">@_actor.MovieCharacters.Count movie@(_actor.MovieCharacters.Count != 1 ? "s" : "") found</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
@if (_matchedMovie != null)
|
@if (_matchedMovie != null)
|
||||||
{
|
{
|
||||||
<div class="card m-2" style="width: 18rem;">
|
<div class="matched-movie-section">
|
||||||
<img class="card-img-top" src="@($"https://image.tmdb.org/t/p/w500{_matchedMovie.MoviePosterPath}")" alt="Movie Poster">
|
<h4 class="section-title">Your Search Match</h4>
|
||||||
<div class="card-body">
|
<div class="movie-card featured">
|
||||||
<h5 class="card-title">@_matchedMovie.MovieTitle</h5>
|
<div class="poster-container">
|
||||||
<h10 class="card-text">@_matchedMovie.CharacterName</h10>
|
<img class="poster-image"
|
||||||
<br />
|
src="@($"https://image.tmdb.org/t/p/w500{_matchedMovie.MoviePosterPath}")"
|
||||||
<small class="card-text">@_matchedMovie.Franchise</small>
|
alt="@_matchedMovie.MovieTitle Poster"
|
||||||
<small class="text-muted">Release Date: @_matchedMovie.ReleaseDate</small>
|
loading="lazy" />
|
||||||
|
<div class="match-badge">✨ Match</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-content">
|
||||||
|
<h5 class="movie-title">@_matchedMovie.MovieTitle</h5>
|
||||||
|
<div class="character-name">as @_matchedMovie.CharacterName</div>
|
||||||
|
@if (!string.IsNullOrEmpty(_matchedMovie.Overview?.Trim()))
|
||||||
|
{
|
||||||
|
<p class="movie-overview expanded">
|
||||||
|
@_matchedMovie.Overview.Trim()
|
||||||
|
</p>
|
||||||
|
}
|
||||||
|
<div class="release-date">Released: @_matchedMovie.ReleaseDate</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
@foreach (var franchiseGroup in _actor.MovieCharacters
|
<div class="filmography-section">
|
||||||
|
<h4 class="section-title">Complete Movie Filmography</h4>
|
||||||
|
|
||||||
|
@foreach (var group in _actor.MovieCharacters
|
||||||
.GroupBy(mc => mc.Franchise)
|
.GroupBy(mc => mc.Franchise)
|
||||||
.OrderBy(g => g.Key == "Standalone")
|
.OrderBy(g => g.Key == "Standalone")
|
||||||
.ThenBy(g => g.Key))
|
.ThenBy(g => g.Key))
|
||||||
{
|
{
|
||||||
<h5 class="mt-4">@franchiseGroup.Key</h5>
|
@if (group.Key != "Standalone")
|
||||||
<div class="d-flex flex-wrap">
|
|
||||||
@foreach (var movieCharacter in (franchiseGroup.Key == "Standalone"
|
|
||||||
? franchiseGroup.OrderByDescending(mc =>
|
|
||||||
DateTime.TryParse(mc.ReleaseDate, out var d) ? d : DateTime.MinValue)
|
|
||||||
: franchiseGroup.OrderBy(mc =>
|
|
||||||
DateTime.TryParse(mc.ReleaseDate, out var d) ? d : DateTime.MaxValue)))
|
|
||||||
{
|
{
|
||||||
<div class="card m-2" style="width: 18rem;">
|
<div class="franchise-group">
|
||||||
<img class="card-img-top" src="@($"https://image.tmdb.org/t/p/w500{movieCharacter.MoviePosterPath}")" alt="Movie Poster">
|
<h5 class="franchise-title">@group.Key Collection</h5>
|
||||||
<div class="card-body">
|
</div>
|
||||||
<h5 class="card-title">@movieCharacter.MovieTitle</h5>
|
}
|
||||||
<h10 class="card-text">@movieCharacter.CharacterName</h10>
|
|
||||||
<p class="text-muted">@movieCharacter.Overview</p>
|
<div class="movies-grid">
|
||||||
<small class="text-muted">Release Date: @movieCharacter.ReleaseDate</small>
|
@foreach (var movie in (group.Key == "Standalone"
|
||||||
|
? group.OrderByDescending(mc => DateTime.TryParse(mc.ReleaseDate, out var d) ? d : DateTime.MinValue)
|
||||||
|
: group.OrderBy(mc => DateTime.TryParse(mc.ReleaseDate, out var d) ? d : DateTime.MaxValue)))
|
||||||
|
{
|
||||||
|
<div class="movie-card @(movie == _matchedMovie ? "matched" : "")">
|
||||||
|
<div class="poster-container">
|
||||||
|
<img class="poster-image"
|
||||||
|
src="@($"https://image.tmdb.org/t/p/w500{movie.MoviePosterPath}")"
|
||||||
|
alt="@movie.MovieTitle Poster"
|
||||||
|
loading="lazy" />
|
||||||
|
<div class="poster-overlay">
|
||||||
|
<div class="overlay-content">
|
||||||
|
<span class="view-details">View Details</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-content">
|
||||||
|
<h5 class="movie-title">@movie.MovieTitle</h5>
|
||||||
|
<div class="character-name">as @movie.CharacterName</div>
|
||||||
|
@if (!string.IsNullOrEmpty(movie.Overview))
|
||||||
|
{
|
||||||
|
<div class="overview-container">
|
||||||
|
<p class="movie-overview @(IsOverviewExpanded(movie.MovieTitle) ? "expanded" : "truncated")">
|
||||||
|
@movie.Overview
|
||||||
|
</p>
|
||||||
|
@if (movie.Overview.Length > 150)
|
||||||
|
{
|
||||||
|
<button class="read-more-btn" @onclick="() => ToggleOverview(movie.MovieTitle)">
|
||||||
|
@(IsOverviewExpanded(movie.MovieTitle) ? "Show less" : "Read more")
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
<div class="release-date">@movie.ReleaseDate</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
}
|
</div>
|
||||||
else
|
</div>
|
||||||
{
|
}
|
||||||
<p>Enter a movie title and character name to begin your search.</p>
|
else if (!string.IsNullOrEmpty(_movieTitle) || !string.IsNullOrEmpty(_characterName))
|
||||||
}
|
{
|
||||||
|
<div class="no-results">
|
||||||
|
<div class="no-results-icon">🔍</div>
|
||||||
|
<h4>No matches found</h4>
|
||||||
|
<p>Try adjusting your search terms or check the spelling</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<div class="welcome-state">
|
||||||
|
<div class="welcome-icon">🎬</div>
|
||||||
|
<h4>Ready to explore?</h4>
|
||||||
|
<p>Enter a movie title and character name to discover an actor's complete film journey</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.search-container {
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 2rem 1rem;
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
|
min-height: 100vh;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-container::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><circle cx="50" cy="50" r="2" fill="rgba(255,255,255,0.1)"/><circle cx="20" cy="20" r="1" fill="rgba(255,255,255,0.08)"/><circle cx="80" cy="30" r="1.5" fill="rgba(255,255,255,0.06)"/></svg>') repeat;
|
||||||
|
opacity: 0.3;
|
||||||
|
pointer-events: none;
|
||||||
|
animation: float 20s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@@keyframes float {
|
||||||
|
0%, 100%
|
||||||
|
|
||||||
|
{
|
||||||
|
transform: translateY(0px) rotate(0deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
50% {
|
||||||
|
transform: translateY(-20px) rotate(180deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-header {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 2.5rem;
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navigation-toggle {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
animation: slideInDown 0.8s ease-out 0.2s both;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-btn {
|
||||||
|
background: rgba(255,255,255,0.15);
|
||||||
|
color: white;
|
||||||
|
text-decoration: none;
|
||||||
|
padding: 0.75rem 1.5rem;
|
||||||
|
border-radius: 2rem;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
border: 1px solid rgba(255,255,255,0.2);
|
||||||
|
box-shadow: 0 4px 15px rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-btn:hover {
|
||||||
|
background: rgba(255,255,255,0.25);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 8px 25px rgba(0,0,0,0.15);
|
||||||
|
color: white;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-btn:active {
|
||||||
|
transform: translateY(0px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-icon {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-btn:hover .nav-icon {
|
||||||
|
transform: scale(1.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.shows-btn {
|
||||||
|
background: linear-gradient(135deg, rgba(79, 70, 229, 0.3), rgba(124, 58, 237, 0.3));
|
||||||
|
}
|
||||||
|
|
||||||
|
.shows-btn:hover {
|
||||||
|
background: linear-gradient(135deg, rgba(79, 70, 229, 0.5), rgba(124, 58, 237, 0.5));
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-title {
|
||||||
|
font-size: 2.5rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: white;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
|
||||||
|
animation: slideInDown 1s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@@keyframes slideInDown {
|
||||||
|
from
|
||||||
|
|
||||||
|
{
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(-50px);
|
||||||
|
}
|
||||||
|
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-subtitle {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
color: rgba(255,255,255,0.9);
|
||||||
|
margin: 0;
|
||||||
|
animation: fadeIn 1.5s ease-out 0.3s both;
|
||||||
|
}
|
||||||
|
|
||||||
|
@@keyframes fadeIn {
|
||||||
|
from
|
||||||
|
|
||||||
|
{
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-form {
|
||||||
|
background: rgba(255,255,255,0.95);
|
||||||
|
border-radius: 1.5rem;
|
||||||
|
padding: 2rem;
|
||||||
|
box-shadow: 0 20px 40px rgba(0,0,0,0.1);
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
border: 1px solid rgba(255,255,255,0.2);
|
||||||
|
animation: slideInUp 1s ease-out 0.6s both;
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@@keyframes slideInUp {
|
||||||
|
from
|
||||||
|
|
||||||
|
{
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(30px);
|
||||||
|
}
|
||||||
|
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-group {
|
||||||
|
display: flex;
|
||||||
|
gap: 1rem;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-wrapper {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 250px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-input {
|
||||||
|
width: 100%;
|
||||||
|
padding: 1rem 1rem 1rem 3.5rem;
|
||||||
|
border: 2px solid #e1e5e9;
|
||||||
|
border-radius: 1rem;
|
||||||
|
font-size: 1rem;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
background: #fafbfc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-input:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: #667eea;
|
||||||
|
background: white;
|
||||||
|
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1), 0 8px 25px rgba(102, 126, 234, 0.15);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-icon {
|
||||||
|
position: absolute;
|
||||||
|
left: 1.2rem;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
font-size: 1.3rem;
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-wrapper:focus-within .input-icon {
|
||||||
|
transform: translateY(-50%) scale(1.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-btn {
|
||||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
|
border: none;
|
||||||
|
color: white;
|
||||||
|
padding: 1rem 2.5rem;
|
||||||
|
border-radius: 1rem;
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
min-width: 160px;
|
||||||
|
justify-content: center;
|
||||||
|
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-btn:hover:not(:disabled) {
|
||||||
|
transform: translateY(-3px);
|
||||||
|
box-shadow: 0 12px 35px rgba(102, 126, 234, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-btn:active:not(:disabled) {
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-btn:disabled {
|
||||||
|
opacity: 0.7;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spinner {
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
border: 2px solid rgba(255,255,255,0.3);
|
||||||
|
border-top: 2px solid white;
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: spin 1s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@@keyframes spin {
|
||||||
|
0%
|
||||||
|
|
||||||
|
{
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-state, .welcome-state, .no-results {
|
||||||
|
text-align: center;
|
||||||
|
padding: 3rem 2rem;
|
||||||
|
color: rgba(255,255,255,0.9);
|
||||||
|
animation: fadeIn 0.5s ease-out;
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-animation {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-dot {
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
background: white;
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: bounce 1.4s ease-in-out infinite both;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-dot:nth-child(1) {
|
||||||
|
animation-delay: -0.32s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-dot:nth-child(2) {
|
||||||
|
animation-delay: -0.16s;
|
||||||
|
}
|
||||||
|
|
||||||
|
@@keyframes bounce {
|
||||||
|
0%, 80%, 100%
|
||||||
|
|
||||||
|
{
|
||||||
|
transform: scale(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
40% {
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.welcome-icon, .no-results-icon {
|
||||||
|
font-size: 4rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
animation: pulse 2s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@@keyframes pulse {
|
||||||
|
0%, 100%
|
||||||
|
|
||||||
|
{
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
50% {
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.results-section {
|
||||||
|
margin-top: 2rem;
|
||||||
|
animation: slideInUp 0.8s ease-out;
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actor-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 1rem;
|
||||||
|
background: rgba(255,255,255,0.1);
|
||||||
|
padding: 1.5rem 2rem;
|
||||||
|
border-radius: 1rem;
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.actor-name {
|
||||||
|
font-size: 2rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: white;
|
||||||
|
margin: 0;
|
||||||
|
text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.results-count {
|
||||||
|
background: rgba(255,255,255,0.2);
|
||||||
|
color: white;
|
||||||
|
padding: 0.5rem 1.5rem;
|
||||||
|
border-radius: 2rem;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
border: 1px solid rgba(255,255,255,0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.matched-movie-section {
|
||||||
|
margin-bottom: 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: white;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
text-shadow: 1px 1px 2px rgba(0,0,0,0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.franchise-group {
|
||||||
|
margin: 2rem 0 1rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.franchise-title {
|
||||||
|
font-size: 1.3rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: white;
|
||||||
|
margin: 0;
|
||||||
|
padding: 1rem 1.5rem;
|
||||||
|
background: rgba(255,255,255,0.1);
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
border-left: 4px solid rgba(255,255,255,0.5);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.movies-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
|
||||||
|
gap: 1.5rem;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.movie-card {
|
||||||
|
background: white;
|
||||||
|
border-radius: 1.2rem;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 8px 25px rgba(0,0,0,0.15);
|
||||||
|
transition: all 0.4s ease;
|
||||||
|
border: 1px solid rgba(255,255,255,0.2);
|
||||||
|
position: relative;
|
||||||
|
transform-style: preserve-3d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.movie-card:hover {
|
||||||
|
transform: translateY(-8px) scale(1.02);
|
||||||
|
box-shadow: 0 20px 40px rgba(0,0,0,0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
.movie-card.featured {
|
||||||
|
border: 2px solid rgba(255,255,255,0.5);
|
||||||
|
box-shadow: 0 12px 35px rgba(102, 126, 234, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
.movie-card.matched {
|
||||||
|
border: 2px solid #10b981;
|
||||||
|
}
|
||||||
|
|
||||||
|
.poster-container {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
aspect-ratio: 2/3;
|
||||||
|
overflow: hidden;
|
||||||
|
background: linear-gradient(45deg, #f0f0f0, #e0e0e0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.poster-image {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
transition: all 0.4s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.poster-overlay {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: linear-gradient(to bottom, transparent 0%, rgba(0,0,0,0.8) 100%);
|
||||||
|
opacity: 0;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-end;
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.movie-card:hover .poster-overlay {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.movie-card:hover .poster-image {
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.overlay-content {
|
||||||
|
color: white;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.match-badge {
|
||||||
|
position: absolute;
|
||||||
|
top: 0.8rem;
|
||||||
|
right: 0.8rem;
|
||||||
|
background: linear-gradient(135deg, #10b981, #059669);
|
||||||
|
color: white;
|
||||||
|
padding: 0.4rem 1rem;
|
||||||
|
border-radius: 1.5rem;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
font-weight: 600;
|
||||||
|
box-shadow: 0 4px 15px rgba(16, 185, 129, 0.4);
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-content {
|
||||||
|
padding: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.movie-title {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1a1a1a;
|
||||||
|
margin: 0 0 0.5rem 0;
|
||||||
|
line-height: 1.3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.character-name {
|
||||||
|
color: #667eea;
|
||||||
|
font-weight: 500;
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.movie-overview {
|
||||||
|
color: #666;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
line-height: 1.4;
|
||||||
|
margin: 0.75rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.movie-overview.truncated {
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 3;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.movie-overview.expanded {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.read-more-btn {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
color: #667eea;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 0.25rem 0;
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
text-decoration: underline;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.read-more-btn:hover {
|
||||||
|
color: #4c63d2;
|
||||||
|
transform: translateX(5px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.release-date {
|
||||||
|
color: #999;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
@@media (max-width: 768px) {
|
||||||
|
.search-container
|
||||||
|
|
||||||
|
{
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-title {
|
||||||
|
font-size: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-group {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-wrapper {
|
||||||
|
min-width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actor-header {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.movies-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
window.startAnimations = () => {
|
||||||
|
console.log('Starting movie page animations...');
|
||||||
|
|
||||||
|
// Enhanced spinner animation with easing
|
||||||
|
const spinners = document.querySelectorAll('.spinner');
|
||||||
|
spinners.forEach((spinner, index) => {
|
||||||
|
let rotation = 0;
|
||||||
|
const animate = () => {
|
||||||
|
rotation += 6;
|
||||||
|
spinner.style.transform = `rotate(${rotation}deg)`;
|
||||||
|
if (document.body.contains(spinner)) {
|
||||||
|
requestAnimationFrame(animate);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
animate();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Enhanced loading dots with wave effect
|
||||||
|
const loadingDots = document.querySelectorAll('.loading-dot');
|
||||||
|
loadingDots.forEach((dot, index) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
dot.style.animationDelay = `${index * 0.16}s`;
|
||||||
|
}, 50);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add stagger animation to movie cards
|
||||||
|
const movieCards = document.querySelectorAll('.movie-card');
|
||||||
|
movieCards.forEach((card, index) => {
|
||||||
|
card.style.opacity = '0';
|
||||||
|
card.style.transform = 'translateY(30px)';
|
||||||
|
setTimeout(() => {
|
||||||
|
card.style.transition = 'all 0.6s ease';
|
||||||
|
card.style.opacity = '1';
|
||||||
|
card.style.transform = 'translateY(0)';
|
||||||
|
}, index * 100 + 200);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add parallax effect
|
||||||
|
window.addEventListener('scroll', () => {
|
||||||
|
const scrolled = window.pageYOffset;
|
||||||
|
const parallaxElements = document.querySelectorAll('.search-container::before');
|
||||||
|
parallaxElements.forEach(element => {
|
||||||
|
element.style.transform = `translateY(${scrolled * 0.5}px)`;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private string _movieTitle;
|
private string _movieTitle = "";
|
||||||
private string _characterName;
|
private string _characterName = "";
|
||||||
private Actor _actor;
|
private Actor _actor;
|
||||||
private MovieCharacter _matchedMovie;
|
private MovieCharacter _matchedMovie;
|
||||||
private bool _isSearching = false;
|
private bool _isSearching = false;
|
||||||
|
private HashSet<string> _expandedOverviews = new HashSet<string>();
|
||||||
|
|
||||||
|
private bool IsOverviewExpanded(string movieTitle)
|
||||||
|
{
|
||||||
|
return _expandedOverviews.Contains(movieTitle);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ToggleOverview(string movieTitle)
|
||||||
|
{
|
||||||
|
if (_expandedOverviews.Contains(movieTitle))
|
||||||
|
{
|
||||||
|
_expandedOverviews.Remove(movieTitle);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_expandedOverviews.Add(movieTitle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async Task SearchForActorAndCharacters()
|
private async Task SearchForActorAndCharacters()
|
||||||
{
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(_movieTitle) || string.IsNullOrWhiteSpace(_characterName))
|
||||||
|
return;
|
||||||
|
|
||||||
_isSearching = true;
|
_isSearching = true;
|
||||||
_actor = null;
|
_actor = null;
|
||||||
_matchedMovie = null; // reset matched movie
|
_matchedMovie = null;
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(_movieTitle) && !string.IsNullOrWhiteSpace(_characterName))
|
await Task.Delay(100);
|
||||||
|
await JSRuntime.InvokeVoidAsync("startAnimations");
|
||||||
|
|
||||||
|
try
|
||||||
{
|
{
|
||||||
var movies = await MovieService.GetMoviesByTitle(_movieTitle);
|
var movies = await MovieService.GetMoviesByTitle(_movieTitle);
|
||||||
|
|
||||||
foreach (var movie in movies)
|
foreach (var movie in movies)
|
||||||
{
|
{
|
||||||
var credits = await MovieService.GetMovieCreditsByMovieId(movie.Id);
|
var credits = await MovieService.GetMovieCreditsByMovieId(movie.Id);
|
||||||
|
|
||||||
foreach (var castMember in credits.Cast)
|
foreach (var castMember in credits.Cast)
|
||||||
{
|
{
|
||||||
if (castMember.Character.ToLower().Contains(_characterName.ToLower()))
|
if (castMember.Character.Contains(_characterName, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
if (_actor == null)
|
if (_actor == null)
|
||||||
{
|
{
|
||||||
_actor = new Actor(castMember.Name);
|
_actor = new Actor(castMember.Name);
|
||||||
int personId = castMember.Id;
|
await LoadActorFilmography(castMember.Id, movie, castMember);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_actor != null) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Search error: {ex.Message}");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_isSearching = false;
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task LoadActorFilmography(int personId, object originalMovie, object originalCast)
|
||||||
|
{
|
||||||
var filmography = await MovieService.GetActorMovieCreditsById(personId);
|
var filmography = await MovieService.GetActorMovieCreditsById(personId);
|
||||||
|
|
||||||
if (filmography?.Cast != null)
|
if (filmography?.Cast != null)
|
||||||
{
|
{
|
||||||
foreach (var role in filmography.Cast)
|
foreach (var role in filmography.Cast)
|
||||||
@@ -105,52 +891,32 @@ else
|
|||||||
|
|
||||||
var movieCharacter = new MovieCharacter
|
var movieCharacter = new MovieCharacter
|
||||||
{
|
{
|
||||||
MovieTitle = role.Title,
|
MovieTitle = role.Title ?? "Unknown Title",
|
||||||
CharacterName = role.Character,
|
CharacterName = role.Character ?? "Unknown Character",
|
||||||
MoviePosterPath = role.PosterPath,
|
MoviePosterPath = role.PosterPath ?? "",
|
||||||
ReleaseDate = DateTime.TryParse(fullMovie?.ReleaseDate, out var parsedDate)
|
ReleaseDate = DateTime.TryParse(fullMovie?.ReleaseDate, out var parsedDate)
|
||||||
? parsedDate.ToString("MM/dd/yyyy")
|
? parsedDate.ToString("MMM dd, yyyy")
|
||||||
: "N/A",
|
: "Unknown Date",
|
||||||
Overview = fullMovie?.Overview ?? "",
|
Overview = fullMovie?.Overview ?? "",
|
||||||
Franchise = fullMovie?.BelongsToCollection?.Name ?? "Standalone"
|
Franchise = fullMovie?.BelongsToCollection?.Name ?? "Standalone"
|
||||||
};
|
};
|
||||||
|
|
||||||
_actor.MovieCharacters.Add(movieCharacter);
|
_actor.MovieCharacters.Add(movieCharacter);
|
||||||
|
|
||||||
if (_matchedMovie == null)
|
if (_matchedMovie == null &&
|
||||||
{
|
string.Equals(role.Title, GetPropertyValue(originalMovie, "Title")?.ToString(), StringComparison.OrdinalIgnoreCase) &&
|
||||||
if (role.Title == movie.Title && role.Character == castMember.Character)
|
string.Equals(role.Character, GetPropertyValue(originalCast, "Character")?.ToString(), StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
_matchedMovie = movieCharacter;
|
_matchedMovie = movieCharacter;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_isSearching = false;
|
private object GetPropertyValue(object obj, string propertyName)
|
||||||
StateHasChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private async Task<Movie> GetProperMovieBasedOffOfActorInvolvement(string movieActorTitle, CastMember actor)
|
|
||||||
{
|
{
|
||||||
List<Credits> overallCredits = new();
|
if (obj == null) return null;
|
||||||
|
var property = obj.GetType().GetProperty(propertyName);
|
||||||
List<Movie> actorMovies = await MovieService.GetMoviesByTitle(movieActorTitle);
|
return property?.GetValue(obj);
|
||||||
|
|
||||||
foreach (Movie movie in actorMovies)
|
|
||||||
{
|
|
||||||
overallCredits.Add(await MovieService.GetMovieCreditsByMovieId(movie.Id));
|
|
||||||
}
|
|
||||||
|
|
||||||
Credits actorMovieCredits = overallCredits.FirstOrDefault(x => x.Cast.Any(c => c.Id == actor.Id));
|
|
||||||
Movie movie1 = actorMovies.Where(x => x.Id == actorMovieCredits.Id).FirstOrDefault();
|
|
||||||
|
|
||||||
return movie1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6,6 +6,12 @@
|
|||||||
|
|
||||||
<div class="search-container">
|
<div class="search-container">
|
||||||
<div class="search-header">
|
<div class="search-header">
|
||||||
|
<div class="navigation-toggle">
|
||||||
|
<a href="/searchmovies" class="nav-btn movies-btn">
|
||||||
|
<span class="nav-icon">🎬</span>
|
||||||
|
<span class="nav-text">Switch to Movies</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
<h2 class="search-title">Discover Actors & Their Shows</h2>
|
<h2 class="search-title">Discover Actors & Their Shows</h2>
|
||||||
<p class="search-subtitle">Search by TV show and character to explore an actor's complete filmography</p>
|
<p class="search-subtitle">Search by TV show and character to explore an actor's complete filmography</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -74,6 +80,11 @@
|
|||||||
alt="@_matchedShow.ShowTitle Poster"
|
alt="@_matchedShow.ShowTitle Poster"
|
||||||
loading="lazy" />
|
loading="lazy" />
|
||||||
<div class="match-badge">✨ Match</div>
|
<div class="match-badge">✨ Match</div>
|
||||||
|
<div class="poster-overlay">
|
||||||
|
<div class="overlay-content">
|
||||||
|
<span class="view-details">View Details</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<h5 class="show-title">@_matchedShow.ShowTitle</h5>
|
<h5 class="show-title">@_matchedShow.ShowTitle</h5>
|
||||||
@@ -116,6 +127,11 @@
|
|||||||
src="@($"https://image.tmdb.org/t/p/w500{show.ShowPosterPath}")"
|
src="@($"https://image.tmdb.org/t/p/w500{show.ShowPosterPath}")"
|
||||||
alt="@show.ShowTitle Poster"
|
alt="@show.ShowTitle Poster"
|
||||||
loading="lazy" />
|
loading="lazy" />
|
||||||
|
<div class="poster-overlay">
|
||||||
|
<div class="overlay-content">
|
||||||
|
<span class="view-details">View Details</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<h5 class="show-title">@show.ShowTitle</h5>
|
<h5 class="show-title">@show.ShowTitle</h5>
|
||||||
@@ -154,7 +170,7 @@
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
<div class="welcome-state">
|
<div class="welcome-state">
|
||||||
<div class="welcome-icon">🎬</div>
|
<div class="welcome-icon">📺</div>
|
||||||
<h4>Ready to explore?</h4>
|
<h4>Ready to explore?</h4>
|
||||||
<p>Enter a TV show title and character name to discover an actor's complete television journey</p>
|
<p>Enter a TV show title and character name to discover an actor's complete television journey</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -167,37 +183,167 @@
|
|||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
padding: 2rem 1rem;
|
padding: 2rem 1rem;
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||||
|
background: linear-gradient(135deg, #4f46e5 0%, #7c3aed 100%);
|
||||||
|
min-height: 100vh;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-container::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><rect x="10" y="20" width="15" height="10" fill="rgba(255,255,255,0.06)" rx="2"/><rect x="70" y="10" width="20" height="15" fill="rgba(255,255,255,0.04)" rx="2"/><rect x="30" y="70" width="12" height="8" fill="rgba(255,255,255,0.05)" rx="1"/></svg>') repeat;
|
||||||
|
opacity: 0.4;
|
||||||
|
pointer-events: none;
|
||||||
|
animation: float 25s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@@keyframes float {
|
||||||
|
0%, 100%
|
||||||
|
|
||||||
|
{
|
||||||
|
transform: translateY(0px) rotate(0deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
50% {
|
||||||
|
transform: translateY(-30px) rotate(180deg);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-header {
|
.search-header {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin-bottom: 2.5rem;
|
margin-bottom: 2.5rem;
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navigation-toggle {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
animation: slideInDown 0.8s ease-out 0.2s both;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-btn {
|
||||||
|
background: rgba(255,255,255,0.15);
|
||||||
|
color: white;
|
||||||
|
text-decoration: none;
|
||||||
|
padding: 0.75rem 1.5rem;
|
||||||
|
border-radius: 2rem;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
border: 1px solid rgba(255,255,255,0.2);
|
||||||
|
box-shadow: 0 4px 15px rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-btn:hover {
|
||||||
|
background: rgba(255,255,255,0.25);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 8px 25px rgba(0,0,0,0.15);
|
||||||
|
color: white;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-btn:active {
|
||||||
|
transform: translateY(0px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-icon {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-btn:hover .nav-icon {
|
||||||
|
transform: scale(1.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.movies-btn {
|
||||||
|
background: linear-gradient(135deg, rgba(102, 126, 234, 0.3), rgba(118, 75, 162, 0.3));
|
||||||
|
}
|
||||||
|
|
||||||
|
.movies-btn:hover {
|
||||||
|
background: linear-gradient(135deg, rgba(102, 126, 234, 0.5), rgba(118, 75, 162, 0.5));
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-title {
|
.search-title {
|
||||||
font-size: 2.5rem;
|
font-size: 2.5rem;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: #1a1a1a;
|
color: white;
|
||||||
margin-bottom: 0.5rem;
|
margin-bottom: 0.5rem;
|
||||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
|
||||||
-webkit-background-clip: text;
|
animation: slideInDown 1s ease-out;
|
||||||
-webkit-text-fill-color: transparent;
|
}
|
||||||
background-clip: text;
|
|
||||||
|
@@keyframes slideInDown {
|
||||||
|
from
|
||||||
|
|
||||||
|
{
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(-50px);
|
||||||
|
}
|
||||||
|
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-subtitle {
|
.search-subtitle {
|
||||||
font-size: 1.1rem;
|
font-size: 1.1rem;
|
||||||
color: #666;
|
color: rgba(255,255,255,0.9);
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
animation: fadeIn 1.5s ease-out 0.3s both;
|
||||||
|
}
|
||||||
|
|
||||||
|
@@keyframes fadeIn {
|
||||||
|
from
|
||||||
|
|
||||||
|
{
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-form {
|
.search-form {
|
||||||
background: white;
|
background: rgba(255,255,255,0.95);
|
||||||
border-radius: 1rem;
|
border-radius: 1.5rem;
|
||||||
padding: 2rem;
|
padding: 2rem;
|
||||||
box-shadow: 0 8px 25px rgba(0,0,0,0.08);
|
box-shadow: 0 20px 40px rgba(0,0,0,0.1);
|
||||||
margin-bottom: 2rem;
|
margin-bottom: 2rem;
|
||||||
border: 1px solid #f0f0f0;
|
backdrop-filter: blur(10px);
|
||||||
|
border: 1px solid rgba(255,255,255,0.2);
|
||||||
|
animation: slideInUp 1s ease-out 0.6s both;
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@@keyframes slideInUp {
|
||||||
|
from
|
||||||
|
|
||||||
|
{
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(30px);
|
||||||
|
}
|
||||||
|
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-group {
|
.input-group {
|
||||||
@@ -215,9 +361,9 @@
|
|||||||
|
|
||||||
.search-input {
|
.search-input {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 0.875rem 1rem 0.875rem 3rem;
|
padding: 1rem 1rem 1rem 3.5rem;
|
||||||
border: 2px solid #e1e5e9;
|
border: 2px solid #e1e5e9;
|
||||||
border-radius: 0.75rem;
|
border-radius: 1rem;
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
background: #fafbfc;
|
background: #fafbfc;
|
||||||
@@ -225,25 +371,31 @@
|
|||||||
|
|
||||||
.search-input:focus {
|
.search-input:focus {
|
||||||
outline: none;
|
outline: none;
|
||||||
border-color: #667eea;
|
border-color: #4f46e5;
|
||||||
background: white;
|
background: white;
|
||||||
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
|
box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.1), 0 8px 25px rgba(79, 70, 229, 0.15);
|
||||||
|
transform: translateY(-2px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-icon {
|
.input-icon {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 1rem;
|
left: 1.2rem;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
transform: translateY(-50%);
|
transform: translateY(-50%);
|
||||||
font-size: 1.2rem;
|
font-size: 1.3rem;
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-wrapper:focus-within .input-icon {
|
||||||
|
transform: translateY(-50%) scale(1.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-btn {
|
.search-btn {
|
||||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
background: linear-gradient(135deg, #4f46e5 0%, #7c3aed 100%);
|
||||||
border: none;
|
border: none;
|
||||||
color: white;
|
color: white;
|
||||||
padding: 0.875rem 2rem;
|
padding: 1rem 2.5rem;
|
||||||
border-radius: 0.75rem;
|
border-radius: 1rem;
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
@@ -251,13 +403,18 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 0.5rem;
|
gap: 0.5rem;
|
||||||
min-width: 140px;
|
min-width: 160px;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
box-shadow: 0 4px 15px rgba(79, 70, 229, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-btn:hover:not(:disabled) {
|
.search-btn:hover:not(:disabled) {
|
||||||
transform: translateY(-2px);
|
transform: translateY(-3px);
|
||||||
box-shadow: 0 8px 25px rgba(102, 126, 234, 0.3);
|
box-shadow: 0 12px 35px rgba(79, 70, 229, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-btn:active:not(:disabled) {
|
||||||
|
transform: translateY(-1px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-btn:disabled {
|
.search-btn:disabled {
|
||||||
@@ -266,17 +423,34 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.spinner {
|
.spinner {
|
||||||
width: 16px;
|
width: 18px;
|
||||||
height: 16px;
|
height: 18px;
|
||||||
border: 2px solid rgba(255,255,255,0.3);
|
border: 2px solid rgba(255,255,255,0.3);
|
||||||
border-top: 2px solid white;
|
border-top: 2px solid white;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
|
animation: spin 1s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@@keyframes spin {
|
||||||
|
0%
|
||||||
|
|
||||||
|
{
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.loading-state, .welcome-state, .no-results {
|
.loading-state, .welcome-state, .no-results {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 3rem 2rem;
|
padding: 3rem 2rem;
|
||||||
color: #666;
|
color: rgba(255,255,255,0.9);
|
||||||
|
animation: fadeIn 0.5s ease-out;
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.loading-animation {
|
.loading-animation {
|
||||||
@@ -287,19 +461,58 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.loading-dot {
|
.loading-dot {
|
||||||
width: 8px;
|
width: 12px;
|
||||||
height: 8px;
|
height: 12px;
|
||||||
background: #667eea;
|
background: white;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
|
animation: bounce 1.4s ease-in-out infinite both;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-dot:nth-child(1) {
|
||||||
|
animation-delay: -0.32s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-dot:nth-child(2) {
|
||||||
|
animation-delay: -0.16s;
|
||||||
|
}
|
||||||
|
|
||||||
|
@@keyframes bounce {
|
||||||
|
0%, 80%, 100%
|
||||||
|
|
||||||
|
{
|
||||||
|
transform: scale(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
40% {
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.welcome-icon, .no-results-icon {
|
.welcome-icon, .no-results-icon {
|
||||||
font-size: 3rem;
|
font-size: 4rem;
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
|
animation: pulse 2s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@@keyframes pulse {
|
||||||
|
0%, 100%
|
||||||
|
|
||||||
|
{
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
50% {
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.results-section {
|
.results-section {
|
||||||
margin-top: 2rem;
|
margin-top: 2rem;
|
||||||
|
animation: slideInUp 0.8s ease-out;
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.actor-header {
|
.actor-header {
|
||||||
@@ -309,22 +522,29 @@
|
|||||||
margin-bottom: 2rem;
|
margin-bottom: 2rem;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
|
background: rgba(255,255,255,0.1);
|
||||||
|
padding: 1.5rem 2rem;
|
||||||
|
border-radius: 1rem;
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.actor-name {
|
.actor-name {
|
||||||
font-size: 2rem;
|
font-size: 2rem;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: #1a1a1a;
|
color: white;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
.results-count {
|
.results-count {
|
||||||
background: #f0f4ff;
|
background: rgba(255,255,255,0.2);
|
||||||
color: #4c63d2;
|
color: white;
|
||||||
padding: 0.5rem 1rem;
|
padding: 0.5rem 1.5rem;
|
||||||
border-radius: 2rem;
|
border-radius: 2rem;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
font-size: 0.9rem;
|
font-size: 0.9rem;
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
border: 1px solid rgba(255,255,255,0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.matched-show-section {
|
.matched-show-section {
|
||||||
@@ -334,11 +554,12 @@
|
|||||||
.section-title {
|
.section-title {
|
||||||
font-size: 1.5rem;
|
font-size: 1.5rem;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: #1a1a1a;
|
color: white;
|
||||||
margin-bottom: 1.5rem;
|
margin-bottom: 1.5rem;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 0.5rem;
|
gap: 0.5rem;
|
||||||
|
text-shadow: 1px 1px 2px rgba(0,0,0,0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
.franchise-group {
|
.franchise-group {
|
||||||
@@ -348,10 +569,13 @@
|
|||||||
.franchise-title {
|
.franchise-title {
|
||||||
font-size: 1.3rem;
|
font-size: 1.3rem;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: #4c63d2;
|
color: white;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding-left: 1rem;
|
padding: 1rem 1.5rem;
|
||||||
border-left: 4px solid #4c63d2;
|
background: rgba(255,255,255,0.1);
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
border-left: 4px solid rgba(255,255,255,0.5);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.shows-grid {
|
.shows-grid {
|
||||||
@@ -363,22 +587,23 @@
|
|||||||
|
|
||||||
.show-card {
|
.show-card {
|
||||||
background: white;
|
background: white;
|
||||||
border-radius: 1rem;
|
border-radius: 1.2rem;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
box-shadow: 0 4px 15px rgba(0,0,0,0.08);
|
box-shadow: 0 8px 25px rgba(0,0,0,0.15);
|
||||||
transition: all 0.3s ease;
|
transition: all 0.4s ease;
|
||||||
border: 1px solid #f0f0f0;
|
border: 1px solid rgba(255,255,255,0.2);
|
||||||
position: relative;
|
position: relative;
|
||||||
|
transform-style: preserve-3d;
|
||||||
}
|
}
|
||||||
|
|
||||||
.show-card:hover {
|
.show-card:hover {
|
||||||
transform: translateY(-4px);
|
transform: translateY(-8px) scale(1.02);
|
||||||
box-shadow: 0 8px 30px rgba(0,0,0,0.12);
|
box-shadow: 0 20px 40px rgba(0,0,0,0.25);
|
||||||
}
|
}
|
||||||
|
|
||||||
.show-card.featured {
|
.show-card.featured {
|
||||||
border: 2px solid #667eea;
|
border: 2px solid rgba(255,255,255,0.5);
|
||||||
box-shadow: 0 8px 30px rgba(102, 126, 234, 0.15);
|
box-shadow: 0 12px 35px rgba(79, 70, 229, 0.25);
|
||||||
}
|
}
|
||||||
|
|
||||||
.show-card.matched {
|
.show-card.matched {
|
||||||
@@ -390,6 +615,7 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
aspect-ratio: 2/3;
|
aspect-ratio: 2/3;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
background: linear-gradient(45deg, #f0f0f0, #e0e0e0);
|
||||||
}
|
}
|
||||||
|
|
||||||
.poster-image {
|
.poster-image {
|
||||||
@@ -397,27 +623,53 @@
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
background-color: #f8f9fa;
|
background-color: #f8f9fa;
|
||||||
transition: transform 0.3s ease;
|
transition: all 0.4s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.poster-overlay {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: linear-gradient(to bottom, transparent 0%, rgba(0,0,0,0.8) 100%);
|
||||||
|
opacity: 0;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-end;
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.show-card:hover .poster-overlay {
|
||||||
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.show-card:hover .poster-image {
|
.show-card:hover .poster-image {
|
||||||
transform: scale(1.05);
|
transform: scale(1.05);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.overlay-content {
|
||||||
|
color: white;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
.match-badge {
|
.match-badge {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0.5rem;
|
top: 0.8rem;
|
||||||
right: 0.5rem;
|
right: 0.8rem;
|
||||||
background: linear-gradient(135deg, #10b981, #059669);
|
background: linear-gradient(135deg, #10b981, #059669);
|
||||||
color: white;
|
color: white;
|
||||||
padding: 0.3rem 0.8rem;
|
padding: 0.4rem 1rem;
|
||||||
border-radius: 1rem;
|
border-radius: 1.5rem;
|
||||||
font-size: 0.8rem;
|
font-size: 0.8rem;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
|
box-shadow: 0 4px 15px rgba(16, 185, 129, 0.4);
|
||||||
|
z-index: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-content {
|
.card-content {
|
||||||
padding: 1.25rem;
|
padding: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.show-title {
|
.show-title {
|
||||||
@@ -429,7 +681,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.character-name {
|
.character-name {
|
||||||
color: #667eea;
|
color: #4f46e5;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
margin-bottom: 0.75rem;
|
margin-bottom: 0.75rem;
|
||||||
font-size: 0.95rem;
|
font-size: 0.95rem;
|
||||||
@@ -456,18 +708,19 @@
|
|||||||
.read-more-btn {
|
.read-more-btn {
|
||||||
background: none;
|
background: none;
|
||||||
border: none;
|
border: none;
|
||||||
color: #667eea;
|
color: #4f46e5;
|
||||||
font-size: 0.85rem;
|
font-size: 0.85rem;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
padding: 0.25rem 0;
|
padding: 0.25rem 0;
|
||||||
margin-top: 0.5rem;
|
margin-top: 0.5rem;
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
transition: color 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.read-more-btn:hover {
|
.read-more-btn:hover {
|
||||||
color: #4c63d2;
|
color: #3730a3;
|
||||||
|
transform: translateX(5px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.air-date {
|
.air-date {
|
||||||
@@ -477,7 +730,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@@media (max-width: 768px) {
|
@@media (max-width: 768px) {
|
||||||
.search-container {
|
.search-container
|
||||||
|
|
||||||
|
{
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -502,17 +757,16 @@
|
|||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
window.startAnimations = () => {
|
window.startAnimations = () => {
|
||||||
console.log('Starting animations...');
|
console.log('Starting TV show page animations...');
|
||||||
|
|
||||||
// Spinner animation
|
// Enhanced spinner animation with easing
|
||||||
const spinners = document.querySelectorAll('.spinner');
|
const spinners = document.querySelectorAll('.spinner');
|
||||||
console.log('Found spinners:', spinners.length);
|
|
||||||
|
|
||||||
spinners.forEach((spinner, index) => {
|
spinners.forEach((spinner, index) => {
|
||||||
let rotation = 0;
|
let rotation = 0;
|
||||||
const animate = () => {
|
const animate = () => {
|
||||||
@@ -525,36 +779,54 @@
|
|||||||
animate();
|
animate();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Loading dots bounce animation
|
// Enhanced loading dots with wave effect
|
||||||
const loadingDots = document.querySelectorAll('.loading-dot');
|
const loadingDots = document.querySelectorAll('.loading-dot');
|
||||||
console.log('Found loading dots:', loadingDots.length);
|
|
||||||
|
|
||||||
loadingDots.forEach((dot, index) => {
|
loadingDots.forEach((dot, index) => {
|
||||||
let scale = 0;
|
|
||||||
let direction = 1;
|
|
||||||
const delay = index * 300;
|
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const animate = () => {
|
dot.style.animationDelay = `${index * 0.16}s`;
|
||||||
scale += direction * 0.1;
|
}, 50);
|
||||||
if (scale >= 1) {
|
});
|
||||||
scale = 1;
|
|
||||||
direction = -1;
|
|
||||||
} else if (scale <= 0.2) {
|
|
||||||
scale = 0.2;
|
|
||||||
direction = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
dot.style.transform = `scale(${scale})`;
|
// Add stagger animation to show cards
|
||||||
|
const showCards = document.querySelectorAll('.show-card');
|
||||||
|
showCards.forEach((card, index) => {
|
||||||
|
card.style.opacity = '0';
|
||||||
|
card.style.transform = 'translateY(30px)';
|
||||||
|
setTimeout(() => {
|
||||||
|
card.style.transition = 'all 0.6s ease';
|
||||||
|
card.style.opacity = '1';
|
||||||
|
card.style.transform = 'translateY(0)';
|
||||||
|
}, index * 100 + 200);
|
||||||
|
});
|
||||||
|
|
||||||
if (document.body.contains(dot)) {
|
// Add floating animation to icons
|
||||||
requestAnimationFrame(animate);
|
const icons = document.querySelectorAll('.input-icon');
|
||||||
}
|
icons.forEach((icon, index) => {
|
||||||
};
|
setTimeout(() => {
|
||||||
animate();
|
icon.style.animation = 'iconFloat 3s ease-in-out infinite';
|
||||||
}, delay);
|
icon.style.animationDelay = `${index * 0.5}s`;
|
||||||
|
}, 1000);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Add parallax effect
|
||||||
|
window.addEventListener('scroll', () => {
|
||||||
|
const scrolled = window.pageYOffset;
|
||||||
|
const parallaxElements = document.querySelectorAll('.search-container::before');
|
||||||
|
parallaxElements.forEach(element => {
|
||||||
|
element.style.transform = `translateY(${scrolled * 0.5}px)`;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Additional CSS for icon floating animation
|
||||||
|
const style = document.createElement('style');
|
||||||
|
style.textContent = `
|
||||||
|
@@keyframes iconFloat {
|
||||||
|
0%, 100% { transform: translateY(-50%) translateX(0px); }
|
||||||
|
50% { transform: translateY(-50%) translateX(2px); }
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
document.head.appendChild(style);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
@@ -592,7 +864,6 @@
|
|||||||
_matchedShow = null;
|
_matchedShow = null;
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
|
|
||||||
// Start animations after a small delay to ensure DOM is ready
|
|
||||||
await Task.Delay(100);
|
await Task.Delay(100);
|
||||||
await JSRuntime.InvokeVoidAsync("startAnimations");
|
await JSRuntime.InvokeVoidAsync("startAnimations");
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user