Skip to content

Commit ab0b790

Browse files
committed
feat: add rest list control, general improvements and minor bugs
1 parent 45634ae commit ab0b790

9 files changed

Lines changed: 387 additions & 9 deletions

File tree

src/WebUI/Model/Game.cs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
using System;
2+
using WebExpress.WebApp.WebAttribute;
3+
using WebExpress.WebIndex;
4+
5+
namespace WebExpress.Tutorial.WebUI.Model
6+
{
7+
/// <summary>
8+
/// Represents a Monkey Island game entry.
9+
/// </summary>
10+
public class Game : IIndexItem
11+
{
12+
/// <summary>
13+
/// The unique identifier of the game.
14+
/// </summary>
15+
[RestListHidden()]
16+
public Guid Id { get; set; } = Guid.NewGuid();
17+
18+
/// <summary>
19+
/// The title of the game.
20+
/// </summary>
21+
[RestListPrimary("Name")]
22+
public string Name { get; set; }
23+
24+
/// <summary>
25+
/// The release year of the game.
26+
/// </summary>
27+
public int ReleaseYear { get; set; }
28+
29+
/// <summary>
30+
/// A short description of the game.
31+
/// </summary>
32+
public string Description { get; set; }
33+
34+
/// <summary>
35+
/// Indicates whether the game is a remake or original.
36+
/// </summary>
37+
public bool IsRemake { get; set; }
38+
}
39+
}

src/WebUI/Model/ViewModel.cs

Lines changed: 77 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,17 @@ public static class ViewModel
1515
/// <summary>
1616
/// Returns the list of inventories available in Monkey Island.
1717
/// </summary>
18-
public static List<Inventory> MonkeyIslanInventories { get; } = [.. GetMonkeyIslandInventories()];
18+
public static List<Inventory> MonkeyIslandInventories { get; } = [.. GetMonkeyIslandInventories()];
1919

2020
/// <summary>
2121
/// Returns the list of predefined locations in Monkey Island.
2222
/// </summary>
23-
public static List<Location> MonkeyIslanLocations { get; } = [.. GetMonkeyIslandLocations()];
23+
public static List<Location> MonkeyIslandLocations { get; } = [.. GetMonkeyIslandLocations()];
24+
25+
/// <summary>
26+
/// Returns a list of Monkey Island games.
27+
/// </summary>
28+
public static List<Game> MonkeyIslandGames { get; } = [.. GetMonkeyIslandGames()];
2429

2530
/// <summary>
2631
/// Retrieves a collection of characters from the Monkey Island universe.
@@ -706,7 +711,77 @@ public static IEnumerable<Location> GetMonkeyIslandLocations()
706711
Description = "The dread pirate's vessel anchored in hidden waters.",
707712
Island = "Monkey Island"
708713
};
714+
}
715+
716+
/// <summary>
717+
/// Retrieves a collection of Monkey Island games.
718+
/// </summary>
719+
/// <returns>A collection containing the games.</returns>
720+
public static IEnumerable<Game> GetMonkeyIslandGames()
721+
{
722+
yield return new Game
723+
{
724+
Name = "The Secret of Monkey Island",
725+
ReleaseYear = 1990,
726+
Description = "Guybrush Threepwood begins his pirate journey on Mêlée Island and faces the ghost pirate LeChuck.",
727+
IsRemake = false
728+
};
729+
730+
yield return new Game
731+
{
732+
Name = "Monkey Island 2: LeChuck’s Revenge",
733+
ReleaseYear = 1991,
734+
Description = "Guybrush searches for the legendary treasure Big Whoop while LeChuck returns as a zombie.",
735+
IsRemake = false
736+
};
737+
738+
yield return new Game
739+
{
740+
Name = "The Curse of Monkey Island",
741+
ReleaseYear = 1997,
742+
Description = "Guybrush must lift a curse from Elaine and defeat LeChuck once again, now in skeletal form.",
743+
IsRemake = false
744+
};
745+
746+
yield return new Game
747+
{
748+
Name = "Escape from Monkey Island",
749+
ReleaseYear = 2000,
750+
Description = "Guybrush battles corporate villain Ozzie Mandrill to save the pirate way of life.",
751+
IsRemake = false
752+
};
709753

754+
yield return new Game
755+
{
756+
Name = "Tales of Monkey Island",
757+
ReleaseYear = 2009,
758+
Description = "Episodic adventure where Guybrush accidentally unleashes a voodoo plague and must stop it.",
759+
IsRemake = false
760+
};
761+
762+
yield return new Game
763+
{
764+
Name = "Return to Monkey Island",
765+
ReleaseYear = 2022,
766+
Description = "Guybrush finally uncovers the true secret of Monkey Island in a modern, stylized adventure.",
767+
IsRemake = false
768+
};
769+
770+
yield return new Game
771+
{
772+
Name = "The Secret of Monkey Island: Special Edition",
773+
ReleaseYear = 2009,
774+
Description = "Remake of the original with updated graphics, voice acting, and music.",
775+
IsRemake = true
776+
};
777+
778+
yield return new Game
779+
{
780+
Name = "Monkey Island 2: LeChuck’s Revenge – Special Edition",
781+
ReleaseYear = 2010,
782+
Description = "Remake of the second game with enhanced visuals and audio, plus commentary.",
783+
IsRemake = true
784+
};
710785
}
711786
}
712787
}

src/WebUI/WWW/Api/1/MonkeyIslandCharacters.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,20 +45,20 @@ public MonkeyIslandCharacters(ISitemapManager sitemapManager, IApplicationContex
4545
/// </summary>
4646
/// <param name="request">The request object containing the criteria for retrieving options. Cannot be null.</param>
4747
/// <param name="row">The row object for which options are being retrieved. Cannot be null.</param>
48-
public override IEnumerable<RestApiCrudTableRowOption> GetOptions(Request request, Character row)
48+
public override IEnumerable<RestApiCrudOption> GetOptions(Request request, Character row)
4949
{
50-
yield return new RestApiCrudTableRowOptionHeader(request)
50+
yield return new RestApiCrudOptionHeader(request)
5151
{
5252
Label = "webexpress.webapp:header.setting.label"
5353
};
5454

55-
yield return new RestApiCrudTableRowOptionEdit(request)
55+
yield return new RestApiCrudOptionEdit(request)
5656
{
5757
Uri = _formUri
5858
};
5959

60-
yield return new RestApiCrudTableRowOptionSeperator(request);
61-
yield return new RestApiCrudTableRowOptionDelete(request);
60+
yield return new RestApiCrudOptionSeperator(request);
61+
yield return new RestApiCrudOptionDelete(request);
6262
}
6363

6464
/// <summary>
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
using System.Collections.Generic;
2+
using System.Linq;
3+
using WebExpress.Tutorial.WebUI.Model;
4+
using WebExpress.Tutorial.WebUI.WWW.Controls;
5+
using WebExpress.WebApp.WebRestApi;
6+
using WebExpress.WebCore.WebApplication;
7+
using WebExpress.WebCore.WebAttribute;
8+
using WebExpress.WebCore.WebMessage;
9+
using WebExpress.WebCore.WebRestApi;
10+
using WebExpress.WebCore.WebSitemap;
11+
12+
namespace WebExpress.Tutorial.WebUI.WWW.Api._1
13+
{
14+
/// <summary>
15+
/// Represents a REST API table for managing and retrieving data about Monkey Island games.
16+
/// </summary>
17+
[Title("Monkey Island Games")]
18+
[Method(CrudMethod.GET)]
19+
[Method(CrudMethod.DELETE)]
20+
[Method(CrudMethod.PUT)]
21+
public sealed class MonkeyIslandGames : RestApiCrudList<Game>
22+
{
23+
private readonly string _formUri;
24+
25+
/// <summary>
26+
/// Initializes a new instance of the class.
27+
/// </summary>
28+
/// <param name="sitemapManager">The sitemap manager used to retrieve URIs for the application context.</param>
29+
/// <param name="applicationContext">The application context containing the current state of the application.</param>
30+
public MonkeyIslandGames(ISitemapManager sitemapManager, IApplicationContext applicationContext)
31+
{
32+
var uri = sitemapManager.GetUri<RestList>(applicationContext);
33+
_formUri = uri?.SetFragment("myListForm")?.ToString();
34+
35+
Data = ViewModel.MonkeyIslandGames;
36+
}
37+
38+
/// <summary>
39+
/// Retrieves a collection of options.
40+
/// </summary>
41+
public override IEnumerable<RestApiCrudOption> GetOptions(Request request, Game row)
42+
{
43+
yield return new RestApiCrudOptionHeader(request)
44+
{
45+
Label = "webexpress.webapp:header.setting.label"
46+
};
47+
48+
yield return new RestApiCrudOptionEdit(request)
49+
{
50+
Uri = _formUri
51+
};
52+
53+
yield return new RestApiCrudOptionSeperator(request);
54+
yield return new RestApiCrudOptionDelete(request);
55+
}
56+
57+
/// <summary>
58+
/// Retrieves a collection of games based on the specified filter.
59+
/// </summary>
60+
public override IEnumerable<Game> GetData(string filter, Request request)
61+
{
62+
if (string.IsNullOrEmpty(filter) || filter == "null")
63+
{
64+
return Data;
65+
}
66+
67+
return Data.Where(x => x.Name.Contains(filter));
68+
}
69+
70+
/// <summary>
71+
/// Performs validation before updating game data.
72+
/// </summary>
73+
public override RestApiValidationResult ValidateUpdateData(Game item, Request request)
74+
{
75+
return new RestApiValidator(request)
76+
.Require(nameof(Game.Name))
77+
.MinLength(nameof(Game.Name), 3)
78+
.Require(nameof(Game.ReleaseYear))
79+
.Result;
80+
}
81+
82+
/// <summary>
83+
/// Updates the game data record.
84+
/// </summary>
85+
public override void UpdateData(Game item, Request request)
86+
{
87+
item.Name = request.GetParameter(nameof(Game.Name))?.Value;
88+
item.Description = request.GetParameter(nameof(Game.Description))?.Value;
89+
item.ReleaseYear = int.TryParse(request.GetParameter(nameof(Game.ReleaseYear))?.Value, out var year) ? year : 0;
90+
}
91+
92+
/// <summary>
93+
/// Deletes a game entry.
94+
/// </summary>
95+
public override void DeleteData(string id, Request request)
96+
{
97+
ViewModel.MonkeyIslandGames.RemoveAll(x => x.Id.ToString() == id);
98+
}
99+
}
100+
}

src/WebUI/WWW/Api/1/MonkeyIslandInventories.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public sealed class MonkeyIslandInventories : RestApiCrudDropdown<Inventory>
2828
/// </param>
2929
public MonkeyIslandInventories(ISitemapManager sitemapManager, IApplicationContext applicationContext)
3030
{
31-
Data = ViewModel.MonkeyIslanInventories;
31+
Data = ViewModel.MonkeyIslandInventories;
3232
}
3333

3434
/// <summary>

src/WebUI/WWW/Api/1/MonkeyIslandLocations.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public sealed class MonkeyIslandLocations : RestApiCrudSelection<Location>
2828
/// </param>
2929
public MonkeyIslandLocations(ISitemapManager sitemapManager, IApplicationContext applicationContext)
3030
{
31-
Data = ViewModel.MonkeyIslanLocations;
31+
Data = ViewModel.MonkeyIslandLocations;
3232
}
3333

3434
/// <summary>

src/WebUI/WWW/Controls/RestList.cs

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
using WebExpress.Tutorial.WebUI.Model;
2+
using WebExpress.Tutorial.WebUI.WebControl;
3+
using WebExpress.Tutorial.WebUI.WebFragment.ControlPage;
4+
using WebExpress.Tutorial.WebUI.WebPage;
5+
using WebExpress.Tutorial.WebUI.WebScope;
6+
using WebExpress.Tutorial.WebUI.WWW.Api._1;
7+
using WebExpress.WebApp.WebControl;
8+
using WebExpress.WebApp.WebScope;
9+
using WebExpress.WebCore.WebAttribute;
10+
using WebExpress.WebCore.WebPage;
11+
using WebExpress.WebCore.WebSitemap;
12+
using WebExpress.WebUI.WebControl;
13+
14+
namespace WebExpress.Tutorial.WebUI.WWW.Controls
15+
{
16+
/// <summary>
17+
/// Represents the list control for the tutorial (REST-backed flat list).
18+
/// </summary>
19+
[Title("RestList")]
20+
[Scope<IScopeGeneral>]
21+
[Scope<IScopeControl>]
22+
[Scope<IScopeControlWebApp>]
23+
public sealed class RestList : PageControl
24+
{
25+
/// <summary>
26+
/// Initializes a new instance of the class.
27+
/// </summary>
28+
/// <param name="pageContext">The context of the page where the list control is used.</param>
29+
/// <param name="sitemapManager">The sitemap manager for managing site navigation.</param>
30+
public RestList(IPageContext pageContext, ISitemapManager sitemapManager)
31+
{
32+
// register demo events that the list control can emit
33+
Stage.AddEvent(Event.MOVE_EVENT);
34+
35+
Stage.Description = @"A `RestList` control is a user interface component that retrieves data from a REST API and exposes CRUD-oriented interactions in a flat list representation. The control automatically fetches data from the API and renders the items as a list. Users can add, modify, delete, and reorder entries, with every action synchronized with the API. In addition, the control provides filtering and pagination for improved accessibility.";
36+
37+
Stage.Controls =
38+
[
39+
new ControlRestList("myList")
40+
{
41+
RestUri = sitemapManager.GetUri<MonkeyIslandGames>(pageContext.ApplicationContext)
42+
}
43+
.Add(new ControlRestFormGame("myListForm")
44+
{
45+
Uri = sitemapManager.GetUri<MonkeyIslandGames>(pageContext.ApplicationContext)
46+
})
47+
];
48+
49+
Stage.DarkControls =
50+
[
51+
new ControlText()
52+
//new ControlRestList("myListDark")
53+
//{
54+
// RestUri = sitemapManager.GetUri<MonkeyIslandCharacters>(pageContext.ApplicationContext)
55+
//}
56+
// .Add(new ControlRestFormCharacter("myListDarkForm")
57+
// {
58+
// Uri = sitemapManager.GetUri<MonkeyIslandCharacters>(pageContext.ApplicationContext)
59+
// })
60+
];
61+
62+
Stage.Code = @"
63+
new ControlRestList(""myList"")
64+
{
65+
RestUri = sitemapManager.GetUri<MonkeyIslandGames>(pageContext.ApplicationContext)
66+
}
67+
.Add(new ControlRestFormGame(""myListForm"")
68+
{
69+
Uri = sitemapManager.GetUri<MonkeyIslandGames>(pageContext.ApplicationContext)
70+
})";
71+
}
72+
}
73+
}

0 commit comments

Comments
 (0)